io.questdb.cairo.wal.seq.SequencerMetadata Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of questdb Show documentation
Show all versions of questdb Show documentation
QuestDB is high performance SQL time series database
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2024 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.cairo.wal.seq;
import io.questdb.cairo.*;
import io.questdb.cairo.sql.TableRecordMetadata;
import io.questdb.cairo.vm.Vm;
import io.questdb.cairo.vm.api.MemoryMARW;
import io.questdb.cairo.vm.api.MemoryMR;
import io.questdb.cairo.wal.WalUtils;
import io.questdb.std.*;
import io.questdb.std.str.Path;
import java.io.Closeable;
import java.util.concurrent.atomic.AtomicLong;
import static io.questdb.cairo.TableUtils.*;
import static io.questdb.cairo.wal.WalUtils.*;
public class SequencerMetadata extends AbstractRecordMetadata implements TableRecordMetadata, Closeable, TableDescriptor {
private final FilesFacade ff;
private final MemoryMARW metaMem;
private final boolean readonly;
private final MemoryMR roMetaMem;
private final AtomicLong structureVersion = new AtomicLong(-1);
private volatile boolean suspended;
private int tableId;
private TableToken tableToken;
private final IntList readColumnOrder = new IntList();
public SequencerMetadata(FilesFacade ff) {
this(ff, false);
}
public SequencerMetadata(FilesFacade ff, boolean readonly) {
this.ff = ff;
this.readonly = readonly;
if (!readonly) {
roMetaMem = metaMem = Vm.getMARWInstance();
} else {
metaMem = null;
roMetaMem = Vm.getCMRInstance();
}
}
public void addColumn(CharSequence columnName, int columnType) {
addColumn0(columnName, columnType);
readColumnOrder.add(columnMetadata.size() - 1);
structureVersion.incrementAndGet();
}
public void changeColumnType(CharSequence columnName, int newType) {
int existingColumnIndex = TableUtils.changeColumnTypeInMetadata(columnName, newType, columnNameIndexMap, columnMetadata);
int readIndex = readColumnOrder.get(existingColumnIndex);
readColumnOrder.add(readIndex);
columnCount++;
structureVersion.incrementAndGet();
}
@Override
public void close() {
reset();
if (metaMem != null) {
metaMem.close(false);
}
Misc.free(roMetaMem);
}
public void create(TableStructure tableStruct, TableToken tableToken, Path path, int pathLen, int tableId) {
create(tableStruct, tableToken, path, pathLen, tableId, true);
}
public void create(TableStructure tableStruct, TableToken tableToken, Path path, int pathLen, int tableId, boolean writeInitialMetadata) {
copyFrom(tableStruct, tableToken, tableId);
openSmallFile(ff, path, pathLen, metaMem, WalUtils.INITIAL_META_FILE_NAME, MemoryTag.MMAP_SEQUENCER_METADATA);
if (writeInitialMetadata) {
TableUtils.writeMetadata(tableStruct, ColumnType.VERSION, tableId, metaMem);
}
metaMem.sync(false);
metaMem.close(true, Vm.TRUNCATE_TO_POINTER);
switchTo(path, pathLen);
}
public void disableDeduplication() {
structureVersion.incrementAndGet();
}
public void dropTable() {
this.structureVersion.set(DROP_TABLE_STRUCTURE_VERSION);
syncToMetaFile();
}
public void enableDeduplicationWithUpsertKeys() {
structureVersion.incrementAndGet();
}
public IntList getReadColumnOrder() {
return readColumnOrder;
}
@Override
public long getMetadataVersion() {
return structureVersion.get();
}
public int getRealColumnCount() {
return columnNameIndexMap.size();
}
@Override
public int getTableId() {
return tableId;
}
@Override
public TableToken getTableToken() {
return tableToken;
}
public boolean isDropped() {
return structureVersion.get() == DROP_TABLE_STRUCTURE_VERSION;
}
@Override
public boolean isWalEnabled() {
return true;
}
public void notifyRenameTable(TableToken tableToken) {
this.tableToken = tableToken;
}
public void open(Path path, int pathLen, TableToken tableToken) {
reset();
openSmallFile(ff, path, pathLen, roMetaMem, META_FILE_NAME, MemoryTag.MMAP_SEQUENCER_METADATA);
// get written data size
if (!readonly) {
metaMem.jumpTo(SEQ_META_OFFSET_WAL_VERSION);
int size = metaMem.getInt(0);
metaMem.jumpTo(size);
}
loadSequencerMetadata(roMetaMem);
structureVersion.set(roMetaMem.getLong(SEQ_META_OFFSET_STRUCTURE_VERSION));
columnCount = columnMetadata.size();
timestampIndex = roMetaMem.getInt(SEQ_META_OFFSET_TIMESTAMP_INDEX);
tableId = roMetaMem.getInt(SEQ_META_TABLE_ID);
suspended = roMetaMem.getBool(SEQ_META_SUSPENDED);
this.tableToken = tableToken;
if (readonly) {
// close early
roMetaMem.close();
}
}
public void removeColumn(CharSequence columnName) {
removeColumnFromMetadata(columnName, columnNameIndexMap, columnMetadata);
structureVersion.incrementAndGet();
}
public void renameColumn(CharSequence columnName, CharSequence newName) {
TableUtils.renameColumnInMetadata(columnName, newName, columnNameIndexMap, columnMetadata);
structureVersion.incrementAndGet();
}
public void renameTable(CharSequence toTableName) {
if (!Chars.equalsIgnoreCaseNc(toTableName, tableToken.getTableName())) {
tableToken = tableToken.renamed(Chars.toString(toTableName));
}
structureVersion.incrementAndGet();
}
public void syncToDisk() {
metaMem.sync(false);
}
public void updateTableToken(TableToken newTableToken) {
assert newTableToken != null;
this.tableToken = newTableToken;
}
private void addColumn0(CharSequence columnName, int columnType) {
final String name = columnName.toString();
if (columnType > 0) {
columnNameIndexMap.put(name, columnMetadata.size());
}
columnMetadata.add(
new TableColumnMetadata(
name,
columnType,
false,
0,
false,
null,
columnMetadata.size(),
false
)
);
columnCount++;
}
private void copyFrom(TableStructure tableStruct, TableToken tableToken, int tableId) {
reset();
this.tableToken = tableToken;
this.timestampIndex = tableStruct.getTimestampIndex();
this.tableId = tableId;
this.suspended = false;
for (int i = 0, n = tableStruct.getColumnCount(); i < n; i++) {
final CharSequence name = tableStruct.getColumnName(i);
final int type = tableStruct.getColumnType(i);
addColumn0(name, type);
readColumnOrder.add(i);
}
this.structureVersion.set(0);
columnCount = columnMetadata.size();
}
private void loadSequencerMetadata(MemoryMR metaMem) {
columnMetadata.clear();
columnNameIndexMap.clear();
readColumnOrder.clear();
try {
final long memSize = Math.min(checkMemSize(metaMem, SEQ_META_OFFSET_COLUMNS), metaMem.getInt(SEQ_META_OFFSET_WAL_LENGTH));
validateMetaVersion(metaMem, SEQ_META_OFFSET_WAL_VERSION, WAL_FORMAT_VERSION);
final int columnCount = TableUtils.getColumnCount(metaMem, SEQ_META_OFFSET_COLUMN_COUNT);
final int timestampIndex = TableUtils.getTimestampIndex(metaMem, SEQ_META_OFFSET_TIMESTAMP_INDEX, columnCount);
// load column types and names
long checkSum = columnCount;
long offset = SEQ_META_OFFSET_COLUMNS;
for (int i = 0; i < columnCount; i++) {
final int type = TableUtils.getColumnType(metaMem, memSize, offset, i);
offset += Integer.BYTES;
final String name = TableUtils.getColumnName(metaMem, memSize, offset, i).toString();
offset += Vm.getStorageLength(name);
if (type > 0) {
columnNameIndexMap.put(name, i);
}
if (ColumnType.isSymbol(Math.abs(type))) {
columnMetadata.add(new TableColumnMetadata(name, type, true, 1024, true, null));
} else {
columnMetadata.add(new TableColumnMetadata(name, type));
}
readColumnOrder.add(i);
checkSum = checkSum * 31 + type;
checkSum = checkSum * 31 + name.hashCode();
}
// validate designated timestamp column
if (timestampIndex != -1) {
final int timestampType = columnMetadata.getQuick(timestampIndex).getType();
if (!ColumnType.isTimestamp(timestampType)) {
throw validationException(metaMem).put("Timestamp column must be TIMESTAMP, but found ").put(ColumnType.nameOf(timestampType));
}
}
if (memSize > offset + 8 + 4) {
long optionalSectionCheckSum = metaMem.getLong(offset);
offset += Long.BYTES;
if (optionalSectionCheckSum == checkSum) {
long records = metaMem.getInt(offset);
offset += Integer.BYTES;
// Optional section about column order
if (memSize - offset >= records * Integer.BYTES) {
readColumnOrder.clear();
for (int i = 0; i < records; i++) {
readColumnOrder.add(metaMem.getInt(offset));
offset += Integer.BYTES;
}
}
}
}
} catch (Throwable e) {
columnNameIndexMap.clear();
columnMetadata.clear();
throw e;
}
}
private void reset() {
columnMetadata.clear();
columnNameIndexMap.clear();
readColumnOrder.clear();
columnCount = 0;
timestampIndex = -1;
tableToken = null;
tableId = -1;
suspended = false;
}
private void switchTo(Path path, int pathLen) {
openSmallFile(ff, path, pathLen, metaMem, META_FILE_NAME, MemoryTag.MMAP_SEQUENCER_METADATA);
syncToMetaFile();
}
boolean isSuspended() {
return suspended;
}
void resumeTable() {
suspended = false;
syncToMetaFile();
}
void suspendTable() {
suspended = true;
syncToMetaFile();
}
void syncToMetaFile() {
metaMem.jumpTo(0);
// Size of metadata
metaMem.putInt(0);
metaMem.putInt(WAL_FORMAT_VERSION);
metaMem.putLong(structureVersion.get());
metaMem.putInt(columnCount);
metaMem.putInt(timestampIndex);
metaMem.putInt(tableId);
metaMem.putBool(suspended);
long checkSum = columnCount;
for (int i = 0; i < columnCount; i++) {
final int columnType = getColumnType(i);
metaMem.putInt(columnType);
metaMem.putStr(getColumnName(i));
checkSum = checkSum * 31 + columnType;
checkSum = checkSum * 31 + getColumnName(i).hashCode();
}
// write column order
metaMem.putLong(checkSum);
metaMem.putInt(readColumnOrder.size());
for (int i = 0, n = readColumnOrder.size(); i < n; i++) {
metaMem.putInt(readColumnOrder.get(i));
}
// update metadata size
metaMem.putInt(0, (int) metaMem.getAppendOffset());
metaMem.sync(false);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy