Skip to content

feat: add Object.create_if_absent atomic insert - #45

Merged
eldonm merged 8 commits into
mainfrom
feat/object-create-if-absent
Sep 19, 2026
Merged

eldonm merged 8 commits into
mainfrom
feat/object-create-if-absent

Conversation

@eldonm

@eldonm eldonm commented Sep 18, 2026

Copy link
Copy Markdown
Member

Summary

  • Add portable Database.insert_if_absent / InsertIfAbsentResult across SQLite, Postgres, MongoDB, JsonDB, DynamoDB, and cache/observable wrappers.
  • Add Object.create_if_absent that never routes through upsert save(), returns (entity, created), and rehydrates the stored winner on conflict.
  • Conflict target is primary id only in v1; SQLite uses INSERT OR IGNORE (not OR REPLACE).
  • Bump to 0.0.20 with changelog, stability, and entity-reference docs.

Test plan

  • pytest tests/db/test_insert_if_absent.py tests/core/test_create_if_absent.py
  • CI full suite on this PR
  • Confirm save() upsert tests still green

Made with Cursor

v1 validates conflict_target=="id" and a non-empty id, then raises
NotImplementedError so custom adapters fail loudly until implemented.
SQLite INSERT OR IGNORE, Postgres ON CONFLICT DO NOTHING RETURNING,
Mongo insert_one/DuplicateKeyError, JsonDB path-lock, DynamoDB
attribute_not_exists. CachingDatabase and ObservableDatabase forward.
Persists via Database.insert_if_absent (not save), rehydrates the stored
winner on conflict, and flushes DeferredSaveMixin only when created.
SQLite/JsonDB concurrency, secondary-unique OR REPLACE hazard,
wrapper forwarding, Mongo/Dynamo mocks, Object rehydrate/deferred flush,
and Postgres integration cases.
Bump version.py to 0.0.20; document InsertIfAbsentResult and
Object.create_if_absent in CHANGELOG, SPEC, stability, and entity-reference.
@github-actions

Copy link
Copy Markdown

Benchmark comparison

Threshold: ±25% (informational, does not block merge)

benchmark baseline (s) current (s) delta status
tests/benchmarks/test_deferred_save_benchmarks.py::test_bench_deferred_save_batched_100 0.016629 0.020178 +21.3% OK
tests/benchmarks/test_deferred_save_benchmarks.py::test_bench_immediate_save_100 0.015861 0.018830 +18.7% OK
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_batched_saves_500 0.731977 3.131758 +327.8% REGRESSION (+327.8%)
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_count_empty_query 1.586718 9.236494 +482.1% REGRESSION (+482.1%)
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_count_filtered 1.274647 4.377951 +243.5% REGRESSION (+243.5%)
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_find_filtered 0.728067 3.294064 +352.4% REGRESSION (+352.4%)
tests/benchmarks/test_jsondb_benchmarks.py::test_bench_jsondb_save_throughput 0.001973 0.007014 +255.5% REGRESSION (+255.5%)
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_count_empty 0.184939 0.219028 +18.4% OK
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_count_fallback_via_regex 0.248176 0.289107 +16.5% OK
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_count_pushdown 0.212034 0.233636 +10.2% OK
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_find_fallback_via_regex 0.216706 0.264057 +21.8% OK
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_find_pushdown 0.176563 0.232817 +31.9% REGRESSION (+31.9%)
tests/benchmarks/test_sqlite_benchmarks.py::test_bench_sqlite_sort_limit_pushdown 0.215163 0.250423 +16.4% OK

@eldonm eldonm self-assigned this Sep 19, 2026
@eldonm
eldonm merged commit b808541 into main Sep 19, 2026
6 checks passed
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