Skip to content
This repository was archived by the owner on Mar 29, 2026. It is now read-only.

Sph 2026 03 19.02#57

Merged
sphildreth merged 4 commits into
mainfrom
sph-2026-03-19.02
Mar 20, 2026
Merged

Sph 2026 03 19.02#57
sphildreth merged 4 commits into
mainfrom
sph-2026-03-19.02

Conversation

@sphildreth

Copy link
Copy Markdown
Owner

This pull request primarily updates the DecentDB bindings and packages to version 1.8.1 across all supported languages and platforms. In addition to the version bump, it introduces a new Python smoke test for lifecycle leak detection and adds a comprehensive review document detailing memory leak findings and recommendations for the DecentDB core. The most notable changes are grouped below:

Version bump and packaging updates

  • All bindings and packages (Dart, Java, Node.js, Python) are updated from version 1.8.0 to 1.8.1, including metadata, manifest files, and code constants. This ensures consistency and proper version tracking across all distribution channels. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14]

Python packaging and test changes

  • Removes legacy Python egg-info files, including PKG-INFO, SOURCES.txt, dependency links, entry points, requirements, and top-level module listings, likely as part of a migration to PEP 621/modern packaging. [1] [2] [3] [4] [5] [6]
  • Adds a new smoke test test_lifecycle_leak_smoke.py to check for memory leaks during repeated connection, error, and close cycles, improving lifecycle leak detection coverage for Python bindings.

Documentation and review findings

  • Adds a detailed memory leak review document (MEMORY_LEAK_REVIEW_FINDINGS.md) that summarizes current leak fixes, highlights critical remaining issues (such as thread-safety races, error-path leaks, and test gaps), and provides actionable recommendations for further improvements and tests.

These changes collectively advance DecentDB's reliability, packaging hygiene, and test coverage, while also documenting remaining risks and areas for improvement.

- Introduced lifecycle test support for tracking memory usage and file descriptors.
- Implemented tests for C API lifecycle leak regressions, ensuring proper resource management during database operations.
- Added engine error path tests to verify that failures do not lead to memory leaks or unbounded resource growth.
- Created isolation tests to confirm ACID properties, ensuring readers do not see uncommitted data.
- Developed tests for memory leak scenarios in complex queries, validating that repeated executions do not increase memory usage.
- Enhanced WAL lifecycle tests to ensure proper cleanup and resource reclamation during concurrent operations.
Copilot AI review requested due to automatic review settings March 20, 2026 12:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request bumps DecentDB to v1.8.1 across bindings/packages and adds lifecycle/leak regression coverage, alongside engine/WAL/C-API fixes to improve close/error-path cleanup under ARC and stabilize shared WAL behavior.

Changes:

  • Bump version metadata to 1.8.1 across Nimble, docs, and language bindings (Java/Dart/Node/Python).
  • Harden WAL/engine lifecycle cleanup (error-path WAL handle closure, ARC cycle breaks, shared WAL registry observability) and adjust C ABI text lifetime handling.
  • Add focused lifecycle/leak regression tests (Nim + Python) plus a Python soak runner and updated testing docs.

Reviewed changes

Copilot reviewed 41 out of 44 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/wal/wal.nim Switch reader abort flags to ref Atomic[bool]; add close-time cleanup helpers and cached-writer cycle break utilities.
src/engine.nim Add closeWalHandle, shared WAL registry observability helpers, and a pending-entry scheme for concurrent shared WAL init; clear caches earlier on closeDb.
src/c_api.nim Add textScratch to make decentdb_column_text return NUL-terminated statement-owned storage.
tests/nim/lifecycle_test_support.nim Add common leak amplification + RSS/fd/heap sampling helpers for lifecycle regression tests.
tests/nim/test_wal_lifecycle_leaks.nim Add WAL lifecycle regression tests (reader timeout cleanup, registry refcounts, UAF regression).
tests/nim/test_error_path_lifecycle.nim Add error-path lifecycle regression ensuring repeated open failures don’t leak fds (Linux).
tests/nim/test_engine_lifecycle_leaks.nim Add open/close lifecycle regression exercising savepoints/temp objects/error paths and shared WAL close ordering.
tests/nim/test_engine_error_paths.nim Add error-path regression scaffolding (catalog-init failure) and a placeholder close-failure test.
tests/nim/test_c_api_lifecycle_leaks.nim Add C API open/prepare/step/finalize/close lifecycle regressions (including shared WAL close order).
tests/nim/test_engine_isolation.nim Add concurrent reader/writer isolation smoke test (skips Windows).
tests/nim/test_engine_memory_leak_queries.nim Add an engine query-loop memory leak test for “complex queries”.
tests/nim/test_arc_lifecycle.nim Add an ARC regression asserting WAL cached-writer cycle is cleared on close.
bindings/python/tests/test_lifecycle_leak_smoke.py Add Python RSS-based lifecycle smoke test across cross-connection + error paths.
scripts/soak_open_insert_select_close.py Add configurable Python soak runner for long lifecycle loops with RSS sampling + slope gating.
tests/harness/leak_runner.py Fix CLI argument formatting and isolate per-test db paths/cleanup in leak harness.
decentdb.nimble Bump version and add test_lifecycle / test_arc_leaks tasks running new suites.
docs/about/changelog.md Add 1.8.1 changelog entry.
docs/development/testing.md Document the new Python soak runner usage and options.
docs/development/MEMORY_LEAK_HUNT_REPORT.md Add memory leak hunt report for this pass.
MEMORY_LEAK_REVIEW_FINDINGS.md Add review findings and recommendations document.
bindings/python/pyproject.toml Bump Python package version to 1.8.1.
bindings/python/decentdb.egg-info/PKG-INFO Remove legacy egg-info artifact.
bindings/python/decentdb.egg-info/SOURCES.txt Remove legacy egg-info artifact.
bindings/python/decentdb.egg-info/dependency_links.txt Remove legacy egg-info artifact.
bindings/python/decentdb.egg-info/entry_points.txt Remove legacy egg-info artifact.
bindings/python/decentdb.egg-info/requires.txt Remove legacy egg-info artifact.
bindings/python/decentdb.egg-info/top_level.txt Remove legacy egg-info artifact.
bindings/node/decentdb/package.json Bump Node native addon version to 1.8.1.
bindings/node/decentdb/package-lock.json Bump Node lockfile version fields to 1.8.1.
bindings/node/knex-decentdb/package.json Bump Knex dialect package version to 1.8.1.
bindings/node/knex-decentdb/package-lock.json Bump lockfile to 1.8.1 (also updates node-gyp constraint).
bindings/java/driver/build.gradle Bump JDBC driver version to 1.8.1.
bindings/java/driver/src/main/java/com/decentdb/jdbc/DecentDBDriver.java Bump DRIVER_VERSION constant to 1.8.1.
bindings/java/dbeaver-extension/build.gradle Bump DBeaver extension version to 1.8.1.
bindings/java/dbeaver-extension/META-INF/MANIFEST.MF Bump bundle version and jar reference to 1.8.1.
bindings/dart/dart/pubspec.yaml Bump Dart package version to 1.8.1.
bindings/dart/native/decentdb.h Update engine version comment example to 1.8.1.
bindings/dart/dart/lib/src/database.dart Update engine version comment example to 1.8.1.
bindings/dart/examples/console/pubspec.lock Bump example lockfile version to 1.8.1.
examples/java/run.sh Update example to reference driver jar 1.8.1.
docs/user-guide/dbeaver.md Update referenced version to 1.8.1.
docs/user-guide/comparison.md Update referenced version to 1.8.1.
docs/development/contributing.md Update example “Version” field to 1.8.1.
docs/development/building.md Update release checklist tag examples to 1.8.1.
decentdb.nimble Bump Nim package version to 1.8.1 and add lifecycle test tasks.
Files not reviewed (2)
  • bindings/node/decentdb/package-lock.json: Language not supported
  • bindings/node/knex-decentdb/package-lock.json: Language not supported

Comment thread tests/nim/test_wal_lifecycle_leaks.nim Outdated
Comment on lines +86 to +87
let db1 = openDb(path).value
let txn1 = beginRead(db1.wal)

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

openDb(path).value is used without checking ok. If openDb fails, this test will proceed with an invalid Db value and may crash in a confusing way. Please require openRes.ok (or similar) before accessing .value to keep failures actionable.

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +10
proc makeTempDb(name: string): string =
let path = getTempDir() / name
if fileExists(path): removeFile(path)
if fileExists(path & ".wal"): removeFile(path & ".wal")
path

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test removes path & ".wal", but the engine WAL file uses the -wal suffix (see engine.openDb building walPath = path & "-wal"). As written, a stale -wal file could survive across runs and make the test flaky. Consider reusing makeTempDbPath/removeDbArtifacts from lifecycle_test_support (or at least delete path & "-wal") and use a .ddb-style temp name to match the rest of the suite.

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +29
discard execSql(db, "CREATE TABLE users (id INT PRIMARY KEY, name TEXT, age INT)")
discard execSql(db, "CREATE TABLE orders (id INT PRIMARY KEY, user_id INT, amount FLOAT)")
discard execSql(db, "CREATE INDEX idx_orders_user ON orders(user_id)")

discard execSql(db, "BEGIN")
for i in 1..100:
discard execSql(db, "INSERT INTO users VALUES (" & $i & ", 'User" & $i & "', " & $(20 + (i mod 30)) & ")")
for j in 1..5:
discard execSql(db, "INSERT INTO orders VALUES (" & $(i * 100 + j) & ", " & $i & ", " & $(float(j) * 10.5) & ")")
discard execSql(db, "COMMIT")

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most SQL calls in this test use discard execSql(...) without asserting success. If any DDL/DML fails, the loop can silently do nothing and the memory assertions become meaningless. Please switch these to require execSql(...).ok (or at least check ... .ok) so the test fails on engine errors.

Copilot uses AI. Check for mistakes.
Comment thread src/c_api.nim
Comment on lines +1174 to +1180
if h.textScratch.len < h.currentValues.len:
h.textScratch.setLen(h.currentValues.len)
h.textScratch[idx] = newString(v.bytes.len + 1)
copyMem(addr h.textScratch[idx][0], unsafeAddr v.bytes[0], v.bytes.len)
h.textScratch[idx][v.bytes.len] = '\0'
# IMPORTANT: return pointer into statement-owned scratch storage.
return cast[cstring](unsafeAddr h.textScratch[idx][0])

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

decentdb_column_text allocates a fresh newString on every call, even when repeatedly fetching the same column across rows. This can add significant allocation churn on the C API hot path. Consider reusing existing textScratch[idx] buffers by resizing in-place when needed (only reallocating when capacity is insufficient), or storing a single reusable string buffer per column that you overwrite per step.

Copilot uses AI. Check for mistakes.
Comment thread src/engine.nim
Comment on lines +96 to +110
while true:
acquire(walRegistryLock)
{.locks: [walRegistryLock].}:
if walRegistry.hasKey(canonicalPath):
let entry = walRegistry[canonicalPath]
if entry.wal != nil:
walRegistry[canonicalPath].refCount.inc
let wal = entry.wal
release(walRegistryLock)
return ok(wal)
else:
# Another thread is creating and recovering the WAL. Wait for it.
release(walRegistryLock)
os.sleep(1)
continue

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new acquireSharedWal waiting logic uses a tight loop with os.sleep(1) polling when a WAL entry is pending. This can lead to unnecessary wakeups under contention. If possible, replace polling with a condition variable/event (or at least exponential backoff / longer sleep) so many concurrent openers don’t spin and add latency.

Copilot uses AI. Check for mistakes.
Comment thread tests/nim/test_engine_error_paths.nim Outdated
Comment on lines +47 to +54
test "closeDb clears cache even if vfs.close fails":
let path = makeTempDbPath("engine_close_fail")
removeDbArtifacts(path)
# Testing this pure unit behaviour requires us to mock VFS or verify cache state
# after an error. Since VFS close failure just returns early now we can simulate
# by checking the code. Wait, we can't easily mock VFS without dependency injection.
# We will just verify it via manual inspection since we fixed the logic.
check true

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test currently doesn't validate the stated behavior; it only check true with comments about manual inspection. That makes the suite a no-op and can mask regressions. Please either implement a real failure-injection/mocked VFS close path (e.g., using the existing faulty VFS test utilities) and assert cache clearing + isOpen behavior, or remove/skip this test until it can be made meaningful.

Copilot uses AI. Check for mistakes.
@sphildreth sphildreth merged commit 9113288 into main Mar 20, 2026
9 checks passed
@sphildreth sphildreth deleted the sph-2026-03-19.02 branch March 20, 2026 12:34
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants