Skip to content

perf(cache): scheduler-facing get_cached_request_context + concurrent-init DDL fix - #422

Merged
yyiilluu merged 3 commits into
mainfrom
claude/cache-golden-floyd-plan-60e2e7
Aug 6, 2026
Merged

perf(cache): scheduler-facing get_cached_request_context + concurrent-init DDL fix#422
yyiilluu merged 3 commits into
mainfrom
claude/cache-golden-floyd-plan-60e2e7

Conversation

@yyiilluu

@yyiilluu yyiilluu commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds get_cached_request_context(), a thin accessor over the existing per-org Reflexio cache, for the two hot playbook-aggregation schedulers in the companion Enterprise PR. The accessor preserves the existing config-version probe, invalidation behavior, construction locks, and cache identity.

The PR also fixes the SQLite cold-start races exposed when different org cache keys concurrently initialize the same SQLite database file.

Changes

  • Add and export get_cached_request_context(org_id, storage_base_dir=None).
  • Raise the default in-process cache capacity from 100 to 512, leaving room for two 100-org scheduler pages plus request-path entries.
  • Serialize same-file SQLite connection setup and migrations with bounded, path-keyed lock striping while allowing different database files to initialize concurrently.
  • Replace all three aggregation triggers inside one explicit BEGIN IMMEDIATE transaction; failures roll back the entire replacement, so other connections cannot observe a partial trigger set.
  • Add cache identity/invalidation/concurrency coverage and SQLite atomicity/rollback regression tests.
  • Scope the architecture guidance specifically to the hot playbook-aggregation schedulers.

Test plan

  • Focused cache and SQLite tests: 80 passed.
  • OSS unit/integration suite: 5605 passed, 73 skipped, 6 subtests passed.
  • OSS e2e suite: 47 passed, 51 skipped.
  • Ruff and Pyright: clean.

Related PR

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds cached request-context access, increases cache capacity to 512, synchronizes SQLite initialization per database, and makes aggregation trigger replacement transactional with rollback handling.

Changes

Cached request context

Layer / File(s) Summary
Cached request-context API and lifecycle
reflexio/server/cache/...
The cache exports get_cached_request_context, uses capacity 512, preserves evicted instances, and returns the context from the shared Reflexio instance.
Cache usage guidance and concurrency validation
.claude/rules/reflexio-patterns.md, tests/server/cache/test_reflexio_cache.py, tests/server/services/durable_learning/test_worker.py
Guidance requires cached accessors for endpoints and schedulers. Tests cover reuse, invalidation, version eviction, storage forwarding, capacity, and concurrent construction.

SQLite initialization safety

Layer / File(s) Summary
Per-database initialization locking
reflexio/server/services/storage/sqlite_storage/_base.py, tests/server/cache/test_reflexio_cache.py
SQLite setup and migrations use bounded locks selected by normalized database path. Tests verify same-database serialization and different-database concurrency.
Atomic aggregation trigger replacement
reflexio/server/services/storage/sqlite_storage/playbook/_aggregation.py, tests/server/services/storage/test_playbook_aggregation_state_integration.py
Aggregation triggers use conditional creation within an immediate transaction. Failed initialization rolls back and preserves existing triggers.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Scheduler
  participant get_cached_request_context
  participant get_reflexio
  participant SQLiteStorage
  Scheduler->>get_cached_request_context: request organization context
  get_cached_request_context->>get_reflexio: retrieve cached Reflexio
  get_reflexio-->>get_cached_request_context: shared Reflexio instance
  get_cached_request_context-->>Scheduler: return request_context
  SQLiteStorage->>SQLiteStorage: acquire database-path initialization lock
  SQLiteStorage->>SQLiteStorage: configure connection and run migrations
Loading

Possibly related PRs

Suggested reviewers: guangyu-reflexio, yilu331

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main changes: the scheduler-facing cached context accessor and the concurrent SQLite DDL fix.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/cache-golden-floyd-plan-60e2e7

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@reflexio/server/services/storage/sqlite_storage/playbook/_aggregation.py`:
- Line 114: Update the DDL execution around the trigger definitions at the
aggregation setup points on lines 114, 128, and 168 to use an explicit BEGIN
IMMEDIATE transaction covering every DROP/CREATE replacement, then commit only
after all replacements succeed and roll back on failure. Preserve the existing
trigger definitions while ensuring concurrent writers cannot run between
replacement statements.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c25fd725-5ca0-4525-ac37-9192b151375f

📥 Commits

Reviewing files that changed from the base of the PR and between 3fdf9ba and 76c7ad2.

📒 Files selected for processing (6)
  • .claude/rules/reflexio-patterns.md
  • reflexio/server/cache/__init__.py
  • reflexio/server/cache/reflexio_cache.py
  • reflexio/server/services/storage/sqlite_storage/playbook/_aggregation.py
  • tests/server/cache/test_reflexio_cache.py
  • tests/server/services/durable_learning/test_worker.py

…; default cache size 512

Schedulers that fan out per-org work were building RequestContext directly
per tick (config decrypt + storage client pools + LLM client each time).
The new accessor delegates to get_reflexio() so schedulers share the same
cache entry, per-hit config_version eviction, striped construction locks,
and every existing invalidate_reflexio_cache call site as the request path.

REFLEXIO_CACHE_MAX_SIZE default rises 100 -> 512: fleet-paging schedulers
hold two independent 100-org pages, so a 100-slot LRU would evict warm
request-path entries on fleets above ~100 orgs.
…a init

Concurrent SQLite storage initialization on one db file interleaves the
DROP TRIGGER / CREATE TRIGGER pairs across connections, so one connection's
CREATE lands between the other's DROP and CREATE and raises 'trigger ...
already exists' (or 'database is locked'). CREATE TRIGGER IF NOT EXISTS
keeps the drop-and-recreate refresh semantics for single-writer upgrades
while making same-script concurrent runs benign.

Also hoist the claim-race test's RequestContext construction out of its
racing worker threads: parallel cold construction is exactly what
get_reflexio's construction lock exists to serialize, and the direct test
factory bypasses it. The race under test is the claim-token fence, not
construction.
Serialize same-file SQLite setup across cache-miss constructions and replace aggregation triggers inside one explicit transaction so concurrent connections never observe a partial schema.
@yyiilluu
yyiilluu force-pushed the claude/cache-golden-floyd-plan-60e2e7 branch from 76c7ad2 to 83b6e6f Compare August 6, 2026 18:19
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@yyiilluu
yyiilluu merged commit 6600919 into main Aug 6, 2026
1 check 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