Skip to content

Reduce INSERT allocations via autoincrement key cache and single-row rowValues alias - #327

Merged
RichardKnop merged 1 commit into
mainfrom
refactor/profile-batch-insert
Jun 19, 2026
Merged

Reduce INSERT allocations via autoincrement key cache and single-row rowValues alias#327
RichardKnop merged 1 commit into
mainfrom
refactor/profile-batch-insert

Conversation

@RichardKnop

Copy link
Copy Markdown
Owner

Summary

  • Autoincrement key cache (lastAutoincrementKey atomic.Int64 on Table): eliminates the per-insert SeekLastKey B-tree traversal and its int64→any boxing. Cold start (cache = −1) calls SeekLastKey once; all subsequent autoincrement inserts use cache + 1 directly. Explicit PK inserts on autoincrement tables update the cache via a CAS high-water loop so a mix of explicit and auto-generated keys stays collision-free.
  • Single-row rowValues alias (Table.Insert): when inserting one row whose values are already in column order (the post-prepareInsert fast path), rowValues is aliased directly to values instead of copying into a freshly allocated []OptionalValue. Multi-row statements still pre-allocate the shared reuse buffer as before.

Benchmark results

Benchmark Before After Delta
Insert batch — allocs/op 2,144 1,955 −8.8%
Insert prepared batch — allocs/op 2,143 1,954 −8.8%
Insert multi-values — allocs/op 1,457 1,363 −6.5%
Insert batch — B/op 124 KiB 116 KiB −6.5%
Insert prepared batch — B/op 123 KiB 115 KiB −6.5%

The savings on multi-values are smaller because that path has multiple rows per statement (len(stmt.Inserts) > 1), so the single-row alias optimisation does not apply; only the SeekLastKey elimination contributes.

Test plan

  • All 4333 tests pass (LOG_LEVEL=error go test ./... -count=1)
  • BenchmarkInsert_PreparedBatch, BenchmarkInsert_Batch, BenchmarkInsert_MultiValues run cleanly
  • e2e TestSelect suite (which mixes explicit and autoincrement PK inserts) passes — this was the key correctness check for the high-water-mark CAS in insertPrimaryKey

🤖 Generated with Claude Code

…rowValues alias

Add lastAutoincrementKey atomic.Int64 to Table (initialised to -1) so that
insertAutoincrementedPrimaryKey can skip the per-insert SeekLastKey B-tree
traversal. The cold path (cache == -1) calls SeekLastKey once to seed the
cache; the hot path uses cache+1 directly and stores the new key after a
successful insert, eliminating one O(log N) traversal and one int64→any
boxing per autoincrement INSERT.

Explicit-key inserts into autoincrement tables now update the cache to
max(cache, newKey) via a CAS high-water loop so that a mix of explicit and
auto-generated inserts (e.g. INSERT INTO t (id,...) VALUES (100,...) followed
by autoincrement inserts) still produces monotonically increasing, collision-
free keys.

Table.Insert no longer allocates a rowValues buffer for single-row inserts
arriving in column order (the post-prepareInsert fast path). rowValues is
aliased directly to the values slice, eliminating one make([]OptionalValue, N)
per prepared-statement exec. Multi-row statements still pre-allocate the
shared reuse buffer.

Net effect on batch/prepared-batch INSERT (100 rows/tx):
  allocs: 2,143 → 1,954 (-8.8%), memory: 123 KiB → 115 KiB (-6.5%)
Multi-values INSERT: allocs 1,457 → 1,363 (-6.5%) (single-row optimisation
does not apply; only the SeekLastKey saving contributes).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Code Coverage

Total: 70.4% (threshold: 70%)

Package Coverage
github.com/RichardKnop/minisql 72.7%
github.com/RichardKnop/minisql/benchmarks/cmd/report 83.0%
github.com/RichardKnop/minisql/cmd/minisql 62.6%
github.com/RichardKnop/minisql/e2e_tests [no
github.com/RichardKnop/minisql/internal/minisql 69.5%
github.com/RichardKnop/minisql/internal/parser 78.2%
github.com/RichardKnop/minisql/internal/pkg/logging 100.0%
github.com/RichardKnop/minisql/pkg/bitwise 100.0%
github.com/RichardKnop/minisql/pkg/bloom 95.0%
github.com/RichardKnop/minisql/pkg/crypto 100.0%
github.com/RichardKnop/minisql/pkg/errors 100.0%
github.com/RichardKnop/minisql/pkg/lrucache 85.2%

@RichardKnop
RichardKnop merged commit 6079a7e into main Jun 19, 2026
5 checks passed
@RichardKnop
RichardKnop deleted the refactor/profile-batch-insert branch June 19, 2026 14:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant