Update options

This commit is contained in:
Andrea Cavalli 2022-04-08 14:32:47 +02:00
parent 29210cca80
commit 95d436860f
5 changed files with 149 additions and 67 deletions

View File

@ -13,6 +13,12 @@ interfacesData:
extendInterfaces: [RPCEvent]
ServerBoundResponse:
extendInterfaces: [RPCEvent]
ColumnOptions:
commonGetters:
levels: DatabaseLevel[]
memtableMemoryBudgetBytes: -long
cacheIndexAndFilterBlocks: -boolean
filter: -Filter
# versions must have only numbers, lowercase letters, dots, dashes. Maximum: 99.999.9999
versions:
0.0.0:
@ -84,6 +90,10 @@ versions:
Filter: [
BloomFilter
]
ColumnOptions: [
DefaultColumnOptions,
NamedColumnOptions
]
customTypes:
Path:
javaClass: java.nio.file.Path
@ -226,7 +236,6 @@ versions:
DatabaseOptions:
data:
volumes: DatabaseVolume[]
levels: DatabaseLevel[]
extraFlags: StringMap
absoluteConsistency: boolean
lowMemory: boolean
@ -235,10 +244,24 @@ versions:
allowNettyDirect: boolean
optimistic: boolean
maxOpenFiles: -int
memtableMemoryBudgetBytes: -long
blockCache: -long
cacheIndexAndFilterBlocks: -boolean
spinning: boolean
defaultColumnOptions: DefaultColumnOptions
columnOptions: NamedColumnOptions[]
# Remember to update ColumnOptions common getters
DefaultColumnOptions:
data:
levels: DatabaseLevel[]
memtableMemoryBudgetBytes: -long
cacheIndexAndFilterBlocks: -boolean
filter: -Filter
# Remember to update ColumnOptions common getters
NamedColumnOptions:
data:
columnName: String
levels: DatabaseLevel[]
memtableMemoryBudgetBytes: -long
cacheIndexAndFilterBlocks: -boolean
filter: -Filter
BloomFilter:
data:

View File

@ -0,0 +1,62 @@
package it.cavallium.dbengine.client;
import it.cavallium.data.generator.nativedata.Nullableboolean;
import it.cavallium.data.generator.nativedata.Nullableint;
import it.cavallium.data.generator.nativedata.Nullablelong;
import it.cavallium.dbengine.rpc.current.data.DatabaseOptions;
import it.cavallium.dbengine.rpc.current.data.DatabaseOptionsBuilder;
import it.cavallium.dbengine.rpc.current.data.DefaultColumnOptions;
import it.cavallium.dbengine.rpc.current.data.DefaultColumnOptionsBuilder;
import it.cavallium.dbengine.rpc.current.data.NamedColumnOptions;
import it.cavallium.dbengine.rpc.current.data.NamedColumnOptionsBuilder;
import it.cavallium.dbengine.rpc.current.data.nullables.NullableFilter;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.rocksdb.RocksDB;
public class DefaultDatabaseOptions {
public static DefaultColumnOptions DEFAULT_DEFAULT_COLUMN_OPTIONS = new DefaultColumnOptions(
Collections.emptyList(),
Nullablelong.empty(),
Nullableboolean.empty(),
NullableFilter.empty()
);
public static NamedColumnOptions DEFAULT_NAMED_COLUMN_OPTIONS = new NamedColumnOptions(
new String(RocksDB.DEFAULT_COLUMN_FAMILY, StandardCharsets.UTF_8),
Collections.emptyList(),
Nullablelong.empty(),
Nullableboolean.empty(),
NullableFilter.empty()
);
public static DatabaseOptions DEFAULT_DATABASE_OPTIONS = new DatabaseOptions(List.of(),
Map.of(),
false,
false,
false,
false,
true,
true,
Nullableint.empty(),
Nullablelong.empty(),
false,
DEFAULT_DEFAULT_COLUMN_OPTIONS,
List.of()
);
public static DatabaseOptionsBuilder builder() {
return DatabaseOptionsBuilder.builder(DEFAULT_DATABASE_OPTIONS);
}
public static DefaultColumnOptionsBuilder defaultColumnOptionsBuilder() {
return DefaultColumnOptionsBuilder.builder(DEFAULT_DEFAULT_COLUMN_OPTIONS);
}
public static NamedColumnOptionsBuilder namedColumnOptionsBuilder() {
return NamedColumnOptionsBuilder.builder(DEFAULT_NAMED_COLUMN_OPTIONS);
}
}

View File

@ -16,9 +16,11 @@ import it.cavallium.dbengine.database.LLSnapshot;
import it.cavallium.dbengine.database.LLUtils;
import it.cavallium.dbengine.database.UpdateMode;
import it.cavallium.dbengine.rpc.current.data.Column;
import it.cavallium.dbengine.rpc.current.data.ColumnOptions;
import it.cavallium.dbengine.rpc.current.data.DatabaseLevel;
import it.cavallium.dbengine.rpc.current.data.DatabaseOptions;
import it.cavallium.dbengine.rpc.current.data.DatabaseVolume;
import it.cavallium.dbengine.rpc.current.data.NamedColumnOptions;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@ -39,6 +41,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -144,50 +147,70 @@ public class LLLocalKeyValueDatabase implements LLKeyValueDatabase {
List<ColumnFamilyDescriptor> descriptors = new ArrayList<>();
descriptors.add(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY));
// Check column names validity
for (NamedColumnOptions columnOption : databaseOptions.columnOptions()) {
if (columns.stream().map(Column::name).noneMatch(columnName -> columnName.equals(columnOption.columnName()))) {
throw new IllegalArgumentException(
"Column " + columnOption.columnName() + " does not exist. Available columns: " + columns
.stream()
.map(Column::name)
.collect(Collectors.joining(", ", "[", "]")));
}
}
for (Column column : columns) {
var columnOptions = new ColumnFamilyOptions();
var columnFamilyOptions = new ColumnFamilyOptions();
var columnOptions = databaseOptions
.columnOptions()
.stream()
.filter(opts -> opts.columnName().equals(column.name()))
.findFirst()
.map(opts -> (ColumnOptions) opts)
.orElse(databaseOptions.defaultColumnOptions());
//noinspection ConstantConditions
if (databaseOptions.memtableMemoryBudgetBytes() != null) {
if (columnOptions.memtableMemoryBudgetBytes() != null) {
// about 512MB of ram will be used for level style compaction
columnOptions.optimizeLevelStyleCompaction(databaseOptions.memtableMemoryBudgetBytes().orElse(
columnFamilyOptions.optimizeLevelStyleCompaction(columnOptions.memtableMemoryBudgetBytes().orElse(
databaseOptions.lowMemory()
? (DEFAULT_COMPACTION_MEMTABLE_MEMORY_BUDGET / 4)
: DEFAULT_COMPACTION_MEMTABLE_MEMORY_BUDGET));
}
// https://www.arangodb.com/docs/stable/programs-arangod-rocksdb.html
columnOptions.setMaxBytesForLevelBase((databaseOptions.spinning() ? 512 : 256) * SizeUnit.MB);
columnFamilyOptions.setMaxBytesForLevelBase((databaseOptions.spinning() ? 512 : 256) * SizeUnit.MB);
// https://www.arangodb.com/docs/stable/programs-arangod-rocksdb.html
columnOptions.setMaxBytesForLevelMultiplier(10);
columnFamilyOptions.setMaxBytesForLevelMultiplier(10);
// https://www.arangodb.com/docs/stable/programs-arangod-rocksdb.html
// https://github.com/facebook/rocksdb/wiki/Tuning-RocksDB-on-Spinning-Disks
columnOptions.setLevelCompactionDynamicLevelBytes(true);
columnFamilyOptions.setLevelCompactionDynamicLevelBytes(true);
// https://www.arangodb.com/docs/stable/programs-arangod-rocksdb.html
columnOptions.setLevel0FileNumCompactionTrigger(2);
columnFamilyOptions.setLevel0FileNumCompactionTrigger(2);
// https://www.arangodb.com/docs/stable/programs-arangod-rocksdb.html
columnOptions.setLevel0SlowdownWritesTrigger(20);
columnFamilyOptions.setLevel0SlowdownWritesTrigger(20);
// https://www.arangodb.com/docs/stable/programs-arangod-rocksdb.html
columnOptions.setLevel0StopWritesTrigger(36);
columnFamilyOptions.setLevel0StopWritesTrigger(36);
if (!databaseOptions.levels().isEmpty()) {
var firstLevelOptions = getRocksLevelOptions(databaseOptions.levels().get(0));
columnOptions.setCompressionType(firstLevelOptions.compressionType);
columnOptions.setCompressionOptions(firstLevelOptions.compressionOptions);
if (!columnOptions.levels().isEmpty()) {
var firstLevelOptions = getRocksLevelOptions(columnOptions.levels().get(0));
columnFamilyOptions.setCompressionType(firstLevelOptions.compressionType);
columnFamilyOptions.setCompressionOptions(firstLevelOptions.compressionOptions);
var lastLevelOptions = getRocksLevelOptions(databaseOptions
var lastLevelOptions = getRocksLevelOptions(columnOptions
.levels()
.get(databaseOptions.levels().size() - 1));
columnOptions.setBottommostCompressionType(lastLevelOptions.compressionType);
columnOptions.setBottommostCompressionOptions(lastLevelOptions.compressionOptions);
.get(columnOptions.levels().size() - 1));
columnFamilyOptions.setBottommostCompressionType(lastLevelOptions.compressionType);
columnFamilyOptions.setBottommostCompressionOptions(lastLevelOptions.compressionOptions);
columnOptions.setCompressionPerLevel(databaseOptions
columnFamilyOptions.setCompressionPerLevel(columnOptions
.levels()
.stream()
.map(v -> v.compression().getType())
.toList());
} else {
columnOptions.setNumLevels(7);
columnFamilyOptions.setNumLevels(7);
List<CompressionType> compressionTypes = new ArrayList<>(7);
for (int i = 0; i < 7; i++) {
if (i < 2) {
@ -196,20 +219,20 @@ public class LLLocalKeyValueDatabase implements LLKeyValueDatabase {
compressionTypes.add(CompressionType.LZ4_COMPRESSION);
}
}
columnOptions.setBottommostCompressionType(CompressionType.LZ4_COMPRESSION);
columnOptions.setBottommostCompressionOptions(new CompressionOptions()
columnFamilyOptions.setBottommostCompressionType(CompressionType.LZ4_COMPRESSION);
columnFamilyOptions.setBottommostCompressionOptions(new CompressionOptions()
.setEnabled(true)
.setMaxDictBytes(32768));
columnOptions.setCompressionPerLevel(compressionTypes);
columnFamilyOptions.setCompressionPerLevel(compressionTypes);
}
final BlockBasedTableConfig tableOptions = new BlockBasedTableConfig();
if (!databaseOptions.lowMemory()) {
tableOptions.setOptimizeFiltersForMemory(true);
tableOptions.setVerifyCompression(false);
}
if (databaseOptions.filter().isPresent()) {
var filterOptions = databaseOptions.filter().get();
tableOptions.setVerifyCompression(false);
if (columnOptions.filter().isPresent()) {
var filterOptions = columnOptions.filter().get();
if (filterOptions instanceof it.cavallium.dbengine.rpc.current.data.BloomFilter bloomFilterOptions) {
// If OptimizeFiltersForHits == true: memory size = bitsPerKey * (totalKeys * 0.1)
@ -218,7 +241,7 @@ public class LLLocalKeyValueDatabase implements LLKeyValueDatabase {
tableOptions.setFilterPolicy(bloomFilter);
}
}
boolean cacheIndexAndFilterBlocks = databaseOptions.cacheIndexAndFilterBlocks()
boolean cacheIndexAndFilterBlocks = columnOptions.cacheIndexAndFilterBlocks()
// https://github.com/facebook/rocksdb/wiki/Partitioned-Index-Filters
.orElse(true);
if (databaseOptions.spinning()) {
@ -243,17 +266,17 @@ public class LLLocalKeyValueDatabase implements LLKeyValueDatabase {
// https://github.com/facebook/rocksdb/wiki/Tuning-RocksDB-on-Spinning-Disks
.setBlockSize((databaseOptions.spinning() ? 256 : 16) * SizeUnit.KB);
columnOptions.setTableFormatConfig(tableOptions);
columnOptions.setCompactionPriority(CompactionPriority.MinOverlappingRatio);
if (databaseOptions.filter().isPresent()) {
var filterOptions = databaseOptions.filter().get();
columnFamilyOptions.setTableFormatConfig(tableOptions);
columnFamilyOptions.setCompactionPriority(CompactionPriority.MinOverlappingRatio);
if (columnOptions.filter().isPresent()) {
var filterOptions = columnOptions.filter().get();
if (filterOptions instanceof it.cavallium.dbengine.rpc.current.data.BloomFilter bloomFilterOptions) {
boolean optimizeForHits = bloomFilterOptions.optimizeForHits()
// https://github.com/facebook/rocksdb/wiki/Tuning-RocksDB-on-Spinning-Disks
// https://github.com/EighteenZi/rocksdb_wiki/blob/master/RocksDB-Tuning-Guide.md#throughput-gap-between-random-read-vs-sequential-read-is-much-higher-in-spinning-disks-suggestions=
.orElse(databaseOptions.spinning());
columnOptions.setOptimizeFiltersForHits(optimizeForHits);
columnFamilyOptions.setOptimizeFiltersForHits(optimizeForHits);
}
}
@ -265,7 +288,7 @@ public class LLLocalKeyValueDatabase implements LLKeyValueDatabase {
// // (but the default value is 1, which means that the maximum sstable of each level is the same).
// columnOptions.setTargetFileSizeMultiplier(1);
descriptors.add(new ColumnFamilyDescriptor(column.name().getBytes(StandardCharsets.US_ASCII), columnOptions));
descriptors.add(new ColumnFamilyDescriptor(column.name().getBytes(StandardCharsets.US_ASCII), columnFamilyOptions));
}
// Get databases directory path

View File

@ -2,6 +2,7 @@ package it.cavallium.dbengine;
import static it.cavallium.dbengine.DbTestUtils.MAX_IN_MEMORY_RESULT_ENTRIES;
import static it.cavallium.dbengine.DbTestUtils.ensureNoLeaks;
import static it.cavallium.dbengine.client.DefaultDatabaseOptions.DEFAULT_DATABASE_OPTIONS;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import it.cavallium.data.generator.nativedata.Nullableboolean;
@ -10,6 +11,7 @@ import it.cavallium.data.generator.nativedata.Nullableint;
import it.cavallium.data.generator.nativedata.Nullablelong;
import it.cavallium.dbengine.DbTestUtils.TempDb;
import it.cavallium.dbengine.DbTestUtils.TestAllocator;
import it.cavallium.dbengine.client.DefaultDatabaseOptions;
import it.cavallium.dbengine.client.IndicizerAnalyzers;
import it.cavallium.dbengine.client.IndicizerSimilarities;
import it.cavallium.dbengine.database.ColumnUtils;
@ -21,6 +23,7 @@ import it.cavallium.dbengine.lucene.analyzer.TextFieldsAnalyzer;
import it.cavallium.dbengine.lucene.analyzer.TextFieldsSimilarity;
import it.cavallium.dbengine.rpc.current.data.ByteBuffersDirectory;
import it.cavallium.dbengine.rpc.current.data.DatabaseOptions;
import it.cavallium.dbengine.rpc.current.data.DatabaseOptionsBuilder;
import it.cavallium.dbengine.rpc.current.data.LuceneOptions;
import it.cavallium.dbengine.rpc.current.data.nullables.NullableFilter;
import java.io.IOException;
@ -85,22 +88,7 @@ public class LocalTemporaryDbGenerator implements TemporaryDbGenerator {
return Mono.zip(
conn.getDatabase("testdb",
List.of(ColumnUtils.dictionary("testmap"), ColumnUtils.special("ints"), ColumnUtils.special("longs")),
new DatabaseOptions(List.of(),
List.of(),
Map.of(),
true,
false,
false,
true,
canUseNettyDirect,
false,
Nullableint.of(-1),
Nullablelong.empty(),
Nullablelong.empty(),
Nullableboolean.empty(),
false,
NullableFilter.empty()
)
DefaultDatabaseOptions.builder().allowNettyDirect(canUseNettyDirect).build()
),
conn.getLuceneIndex("testluceneindex1",
LuceneUtils.singleStructure(),

View File

@ -9,6 +9,7 @@ import it.cavallium.data.generator.nativedata.Nullableint;
import it.cavallium.data.generator.nativedata.Nullablelong;
import it.cavallium.dbengine.DbTestUtils.TempDb;
import it.cavallium.dbengine.DbTestUtils.TestAllocator;
import it.cavallium.dbengine.client.DefaultDatabaseOptions;
import it.cavallium.dbengine.client.IndicizerAnalyzers;
import it.cavallium.dbengine.client.IndicizerSimilarities;
import it.cavallium.dbengine.database.ColumnUtils;
@ -56,22 +57,7 @@ public class MemoryTemporaryDbGenerator implements TemporaryDbGenerator {
.zip(
conn.getDatabase("testdb",
List.of(ColumnUtils.dictionary("testmap"), ColumnUtils.special("ints"), ColumnUtils.special("longs")),
DatabaseOptions.of(List.of(),
List.of(),
Map.of(),
true,
false,
false,
true,
canUseNettyDirect,
true,
Nullableint.of(-1),
Nullablelong.empty(),
Nullablelong.empty(),
Nullableboolean.empty(),
false,
NullableFilter.empty()
)
DefaultDatabaseOptions.builder().allowNettyDirect(canUseNettyDirect).build()
),
conn.getLuceneIndex("testluceneindex1",
LuceneUtils.singleStructure(),