perf(storage): add bulk plugin-storage upserts#1098
Conversation
f2a006d to
5851d0a
Compare
Add collection-level bulk methods to the OpenAPI store test stub so it satisfies the expanded PluginStorageCollectionFacade interface.
Greptile SummaryThis PR adds a
Confidence Score: 5/5Safe to merge; the change is well-scoped and all policy-enforcement paths are covered by the new tests. Policy validation (both create and update) is enforced at the ORM layer before any adapter call. Empty-target and empty-update guards exist at both ORM and adapter levels. The native PG/SQLite path uses correct excluded.column references and parameter-aware batching. The ORM fallback for adapters without native upsertMany is logically sound. Cross-tenant write rejection and invalid-shape rejection are both tested. No correctness gaps were found in the changed paths. No files require special attention. test-config.ts bypasses the ORM fallback when calling upsertMany on the lazy test DB, but this is benign since the memory adapter always provides the method. Important Files Changed
Reviews (2): Last reviewed commit: "fix(fumadb): validate bulk upsert confli..." | Re-trigger Greptile |
## Summary - Mirror the upstream bulk-upsert validation hardening from RhysSullivan#1098. - Reject empty `upsertMany` conflict targets at the public FumaDB query boundary. - Add adapter-level empty-target guards for Drizzle and memory adapters. - Add a table-policy regression test for invalid bulk upsert target/update shapes. ## Validation - `bunx vitest run src/query/table-policy.test.ts` from `packages/core/fumadb` - `bunx oxlint --no-ignore --deny-warnings src/query/orm/index.ts src/adapters/drizzle/query.ts src/adapters/memory/index.ts src/query/table-policy.test.ts` from `packages/core/fumadb` - `git diff --check` ## Notes `bun run --cwd packages/core/fumadb typecheck` is currently blocked in this checkout by the existing missing `@libsql/client` import in `src/adapters/drizzle/runtime-ensure.test.ts`. Root oxlint intentionally ignores `packages/core/fumadb/`, so the file-level lint was run from the package with `--no-ignore`.
|
Additional downstream context from my fork: this bulk plugin-storage primitive is what makes storage-heavy plugins practical without hand-rolled adapter paths. High-level shape: AST outline of downstream consumers: semanticSearchIndex
-> jobs.putMany(...)
-> chunks.removeMany(...)
-> chunks.putMany(...)
-> fingerprints.putMany(...)
openapiStore
-> removeOperations(...)
-> appendOperations(...)Fork permalinks:
Call stack: This PR intentionally does not include semantic search. It adds the storage primitive that lets semantic search, OpenAPI operation refresh, and other plugin-maintained indexes use one safe SDK path. |
Summary
Adds a reusable bulk upsert path for FumaDB and routes plugin-storage bulk writes through adapter-level conflict upserts instead of delete-then-create batches.
This keeps the public plugin-storage API small while giving storage-heavy plugins a faster and safer write path for repeated key updates.
Implementation Shape
FumaDB Query Surface
packages/core/fumadb/src/query/index.tsupsertManymirrors the existing single-row upsert shape, but accepts many values and forwards through the same ORM policy boundary as other write operations.ORM Adapter Contract
packages/core/fumadb/src/query/orm/index.tsThe ORM wrapper remains the policy boundary. Callers do not bypass table policy checks by calling adapter methods directly.
Adapter Implementations
The memory adapter is the reference implementation for semantic behavior. The Drizzle adapter maps the same contract to SQL conflict upserts.
Plugin Storage Facade
packages/core/sdk/src/plugin-storage.tsCollection helpers keep plugin authors on collection-local keys while the lower-level facade can still batch entries across collections.
Bulk Write Flow
Policy And Ownership Flow
Scope Notes
Validation
bun run --cwd packages/core/fumadb typecheckbun run --cwd packages/core/sdk typecheckbun run --cwd packages/core/fumadb test src/query/table-policy.test.tsbun run --cwd packages/core/sdk test src/plugin-storage.test.tsgit diff --check upstream/main...HEADoxfmt --checkoxlint -c .oxlintrc.jsonc --deny-warningsFollow-up Scope
JSON-document aggregation and keyset pagination are separate storage/query work and intentionally remain out of this PR.