Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,15 @@ jobs:
ccache -s
make GEN=Ninja nodejs && ccache -s

- name: Install OpenSSL 3 (macOS)
if: ${{ matrix.mac_env }}
run: |
# Pin to OpenSSL 3: Homebrew now also ships openssl@4, which CMake
# would otherwise prefer. The top-level CMakeLists enforces OpenSSL 3
# via a version guard, so point CMake at openssl@3 explicitly.
brew list openssl@3 >/dev/null 2>&1 || brew install openssl@3
echo "OPENSSL_ROOT_DIR=$(brew --prefix openssl@3)" >> "$GITHUB_ENV"

- name: Build Node.js native module (non-Windows)
if: ${{ matrix.platform != 'win32' }}
run: make GEN=Ninja nodejs
Expand Down
19 changes: 10 additions & 9 deletions src_cpp/include/node_database.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <memory>
#include <optional>

#include "main/lbug.h"
#include <napi.h>
Expand All @@ -26,15 +27,15 @@ class NodeDatabase : public Napi::ObjectWrap<NodeDatabase> {

private:
std::string databasePath;
size_t bufferPoolSize;
bool enableCompression;
bool readOnly;
uint64_t maxDBSize;
bool autoCheckpoint;
int64_t checkpointThreshold;
bool throwOnWalReplayFailure;
bool enableChecksums;
bool enableDefaultHashIndex;
std::optional<int64_t> bufferPoolSize;
std::optional<bool> enableCompression;
std::optional<bool> readOnly;
std::optional<int64_t> maxDBSize;
std::optional<bool> autoCheckpoint;
std::optional<int64_t> checkpointThreshold;
std::optional<bool> throwOnWalReplayFailure;
std::optional<bool> enableChecksums;
std::optional<bool> enableDefaultHashIndex;
std::shared_ptr<Database> database;
};

Expand Down
103 changes: 78 additions & 25 deletions src_cpp/node_database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,60 @@ Napi::Object NodeDatabase::Init(Napi::Env env, Napi::Object exports) {
return exports;
}

namespace {

// Null/undefined means "use the engine default"; the setting is left unset.
bool getOptionalInt64(const Napi::CallbackInfo& info, size_t index, int64_t& out) {
if (index >= info.Length() || info[index].IsNull() || info[index].IsUndefined()) {
return false;
}
out = info[index].As<Napi::Number>().Int64Value();
return true;
}

bool getOptionalBool(const Napi::CallbackInfo& info, size_t index, bool& out) {
if (index >= info.Length() || info[index].IsNull() || info[index].IsUndefined()) {
return false;
}
out = info[index].As<Napi::Boolean>().Value();
return true;
}

} // namespace

NodeDatabase::NodeDatabase(const Napi::CallbackInfo& info) : Napi::ObjectWrap<NodeDatabase>(info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
databasePath = info[0].ToString();
bufferPoolSize = info[1].As<Napi::Number>().Int64Value();
enableCompression = info[2].As<Napi::Boolean>().Value();
readOnly = info[3].As<Napi::Boolean>().Value();
maxDBSize = info[4].As<Napi::Number>().Int64Value();
autoCheckpoint = info[5].As<Napi::Boolean>().Value();
checkpointThreshold = info[6].As<Napi::Number>().Int64Value();
throwOnWalReplayFailure = info[7].As<Napi::Boolean>().Value();
enableChecksums = info[8].As<Napi::Boolean>().Value();
enableDefaultHashIndex = info[9].As<Napi::Boolean>().Value();
int64_t intValue = 0;
bool boolValue = false;
if (getOptionalInt64(info, 1, intValue)) {
bufferPoolSize = intValue;
}
if (getOptionalBool(info, 2, boolValue)) {
enableCompression = boolValue;
}
if (getOptionalBool(info, 3, boolValue)) {
readOnly = boolValue;
}
if (getOptionalInt64(info, 4, intValue)) {
maxDBSize = intValue;
}
if (getOptionalBool(info, 5, boolValue)) {
autoCheckpoint = boolValue;
}
if (getOptionalInt64(info, 6, intValue)) {
checkpointThreshold = intValue;
}
if (getOptionalBool(info, 7, boolValue)) {
throwOnWalReplayFailure = boolValue;
}
if (getOptionalBool(info, 8, boolValue)) {
enableChecksums = boolValue;
}
if (getOptionalBool(info, 9, boolValue)) {
enableDefaultHashIndex = boolValue;
}
}

Napi::Value NodeDatabase::InitSync(const Napi::CallbackInfo& info) {
Expand All @@ -54,23 +95,35 @@ Napi::Value NodeDatabase::InitAsync(const Napi::CallbackInfo& info) {
}

void NodeDatabase::InitCppDatabase() {
// Start from the engine defaults and only apply explicitly set settings,
// so engine-side default changes propagate automatically.
auto systemConfig = SystemConfig();
if (bufferPoolSize > 0) {
systemConfig.bufferPoolSize = bufferPoolSize;
}
if (!enableCompression) {
systemConfig.enableCompression = enableCompression;
}
systemConfig.readOnly = readOnly;
if (maxDBSize > 0) {
systemConfig.maxDBSize = maxDBSize;
}
systemConfig.autoCheckpoint = autoCheckpoint;
systemConfig.throwOnWalReplayFailure = throwOnWalReplayFailure;
systemConfig.enableChecksums = enableChecksums;
systemConfig.enableDefaultHashIndex = enableDefaultHashIndex;
if (checkpointThreshold >= 0) {
systemConfig.checkpointThreshold = checkpointThreshold;
if (bufferPoolSize && *bufferPoolSize > 0) {
systemConfig.bufferPoolSize = static_cast<uint64_t>(*bufferPoolSize);
}
if (enableCompression) {
systemConfig.enableCompression = *enableCompression;
}
if (readOnly) {
systemConfig.readOnly = *readOnly;
}
if (maxDBSize && *maxDBSize > 0) {
systemConfig.maxDBSize = static_cast<uint64_t>(*maxDBSize);
}
if (autoCheckpoint) {
systemConfig.autoCheckpoint = *autoCheckpoint;
}
if (throwOnWalReplayFailure) {
systemConfig.throwOnWalReplayFailure = *throwOnWalReplayFailure;
}
if (enableChecksums) {
systemConfig.enableChecksums = *enableChecksums;
}
if (enableDefaultHashIndex) {
systemConfig.enableDefaultHashIndex = *enableDefaultHashIndex;
}
if (checkpointThreshold && *checkpointThreshold >= 0) {
systemConfig.checkpointThreshold = static_cast<uint64_t>(*checkpointThreshold);
}
this->database = std::make_shared<Database>(databasePath, systemConfig);
}
Expand Down
69 changes: 41 additions & 28 deletions src_js/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,53 +11,66 @@ class Database {
*
* @param {String} databasePath path to the database file. If the path is not specified, or empty, or equal to
`:memory:`, the database will be created in memory.
* @param {Number} bufferManagerSize size of the buffer manager in bytes.
* @param {Boolean} enableCompression whether to enable compression.
* @param {Boolean} readOnly if true, database will be opened in read-only mode.
* @param {Number} maxDBSize maximum size of the database file in bytes. Note that
* @param {Number|null} bufferManagerSize size of the buffer manager in bytes. Null (default) uses the engine default.
* @param {Boolean|null} enableCompression whether to enable compression. Null (default) uses the engine default.
* @param {Boolean|null} readOnly if true, database will be opened in read-only mode. Null (default) uses the engine default.
* @param {Number|null} maxDBSize maximum size of the database file in bytes. Note that
* this is introduced temporarily for now to get around with the default 8TB mmap
* address space limit some environment.
* @param {Boolean} autoCheckpoint If true, the database will automatically checkpoint when the size of
* the WAL file exceeds the checkpoint threshold.
* @param {Number} checkpointThreshold The threshold of the WAL file size in bytes. When the size of the
* address space limit some environment. Null (default) uses the engine default.
* @param {Boolean|null} autoCheckpoint If true, the database will automatically checkpoint when the size of
* the WAL file exceeds the checkpoint threshold. Null (default) uses the engine default.
* @param {Number|null} checkpointThreshold The threshold of the WAL file size in bytes. When the size of the
* WAL file exceeds this threshold, the database will checkpoint if autoCheckpoint is true.
* @param {Boolean} throwOnWalReplayFailure If true, any WAL replaying failure when loading the database
* Null (default) uses the engine default.
* @param {Boolean|null} throwOnWalReplayFailure If true, any WAL replaying failure when loading the database
* will throw an error. Otherwise, Lbug will silently ignore the failure and replay up to where
* the error occured.
* @param {Boolean} enableChecksums If true, the database will use checksums to detect corruption in the
* WAL file.
* @param {Boolean} enableDefaultHashIndex If true, node tables create the default primary-key hash index.
* the error occured. Null (default) uses the engine default.
* @param {Boolean|null} enableChecksums If true, the database will use checksums to detect corruption in the
* WAL file. Null (default) uses the engine default.
* @param {Boolean|null} enableDefaultHashIndex If true, node tables create the default primary-key hash index.
* Null (default) uses the engine default.
*
* Any setting left as null/undefined is not forwarded, so the engine default applies and
* engine-side default changes propagate automatically.
*/
constructor(
databasePath,
bufferManagerSize = 0,
enableCompression = true,
readOnly = false,
maxDBSize = 0,
autoCheckpoint = true,
checkpointThreshold = -1,
throwOnWalReplayFailure = true,
enableChecksums = true,
enableDefaultHashIndex = true,
bufferManagerSize = null,
enableCompression = null,
readOnly = null,
maxDBSize = null,
autoCheckpoint = null,
checkpointThreshold = null,
throwOnWalReplayFailure = null,
enableChecksums = null,
enableDefaultHashIndex = null,
) {
if (!databasePath) {
databasePath = ":memory:";
}
else if (typeof databasePath !== "string") {
throw new Error("Database path must be a string.");
}
if (typeof bufferManagerSize !== "number" || bufferManagerSize < 0) {
if (bufferManagerSize != null && (typeof bufferManagerSize !== "number" || bufferManagerSize < 0)) {
throw new Error("Buffer manager size must be a positive integer.");
}
if (typeof maxDBSize !== "number" || maxDBSize < 0) {
if (maxDBSize != null && (typeof maxDBSize !== "number" || maxDBSize < 0)) {
throw new Error("Max DB size must be a positive integer.");
}
if (typeof checkpointThreshold !== "number" || maxDBSize < -1) {
// Note: negative explicit values (e.g. the historical -1 default) are accepted here and
// ignored by the native layer, which keeps the engine default for them.
if (checkpointThreshold != null && typeof checkpointThreshold !== "number") {
throw new Error("Checkpoint threshold must be a positive integer.");
}
bufferManagerSize = Math.floor(bufferManagerSize);
maxDBSize = Math.floor(maxDBSize);
checkpointThreshold = Math.floor(checkpointThreshold);
if (bufferManagerSize != null) {
bufferManagerSize = Math.floor(bufferManagerSize);
}
if (maxDBSize != null) {
maxDBSize = Math.floor(maxDBSize);
}
if (checkpointThreshold != null) {
checkpointThreshold = Math.floor(checkpointThreshold);
}
this._database = new LbugNative.NodeDatabase(
databasePath,
bufferManagerSize,
Expand Down
38 changes: 21 additions & 17 deletions src_js/lbug.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,21 +104,24 @@ export type LbugValue =

/**
* Configuration options for the database system.
*
* Any option left undefined uses the engine default; explicitly set options
* override the engine configuration.
*/
export interface SystemConfig {
/** Size of the buffer pool in bytes */
/** Size of the buffer pool in bytes (engine default if omitted) */
bufferPoolSize?: number;
/** Whether to enable compression */
/** Whether to enable compression (engine default if omitted) */
enableCompression?: boolean;
/** Whether to open the database in read-only mode */
/** Whether to open the database in read-only mode (engine default if omitted) */
readOnly?: boolean;
/** Maximum size of the database in bytes */
/** Maximum size of the database in bytes (engine default if omitted) */
maxDBSize?: number;
/** Whether to enable automatic checkpoints */
/** Whether to enable automatic checkpoints (engine default if omitted) */
autoCheckpoint?: boolean;
/** Threshold for automatic checkpoints */
/** Threshold for automatic checkpoints (engine default if omitted) */
checkpointThreshold?: number;
/** Whether node tables create the default primary-key hash index */
/** Whether node tables create the default primary-key hash index (engine default if omitted) */
enableDefaultHashIndex?: boolean;
}

Expand All @@ -132,17 +135,18 @@ export function json(value: string | LbugValue): LbugValue;
*/
export class Database {
/**
* Constructs a new Database instance.
* Constructs a new Database instance. Any setting left undefined/null uses
* the engine default; explicitly set settings override it.
* @param databasePath Path to the database directory (defaults to ":memory:")
* @param bufferManagerSize Size of the buffer manager in bytes
* @param enableCompression Whether to enable compression
* @param readOnly Whether to open in read-only mode
* @param maxDBSize Maximum size of the database in bytes
* @param autoCheckpoint Whether to enable automatic checkpoints
* @param checkpointThreshold Threshold for automatic checkpoints
* @param throwOnWalReplayFailure Whether WAL replay failures should throw
* @param enableChecksums Whether checksum validation is enabled
* @param enableDefaultHashIndex Whether node tables create the default primary-key hash index
* @param bufferManagerSize Size of the buffer manager in bytes (engine default if omitted)
* @param enableCompression Whether to enable compression (engine default if omitted)
* @param readOnly Whether to open in read-only mode (engine default if omitted)
* @param maxDBSize Maximum size of the database in bytes (engine default if omitted)
* @param autoCheckpoint Whether to enable automatic checkpoints (engine default if omitted)
* @param checkpointThreshold Threshold for automatic checkpoints (engine default if omitted)
* @param throwOnWalReplayFailure Whether WAL replay failures should throw (engine default if omitted)
* @param enableChecksums Whether checksum validation is enabled (engine default if omitted)
* @param enableDefaultHashIndex Whether node tables create the default primary-key hash index (engine default if omitted)
*/
constructor(
databasePath?: string,
Expand Down
Loading