* perf: memoize Family#balance_sheet/investment_statement, cache transactions-index side queries
The account sidebar renders on every page (mobile + desktop, 3 tabs each)
and calls Family#balance_sheet multiple times per render; neither it nor
Family#investment_statement/InvestmentStatement#current_holdings were
memoized, so each call rebuilt the underlying query from scratch. Memoize
both per-user (family sharing means different users must not share a
cached BalanceSheet/InvestmentStatement).
Also cache TransactionsController#index's uncategorized_count and
projected_recurring lookups, which run unconditionally on every request
regardless of whether the underlying data changed, using the same
entries_cache_version-keyed pattern already used elsewhere in the codebase
(e.g. Transaction::Search#totals).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix: address PR #3058 review feedback on cache invalidation and query reuse
- Invalidate transactions-index caches when the current user's AccountShare
access changes, not just on entries/recurring updates (CodeRabbit/Codex
flagged revoked users could see stale data for up to a day).
- Use full-precision timestamps instead of to_i in the cache keys so
same-second updates aren't missed.
- Reuse the already-memoized investment_account_ids in
InvestmentStatement#current_holdings instead of an extra any? query.
- Assert the rendered response instead of a controller instance variable in
the uncategorized_count test, per CodeRabbit nitpick.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix: assert rendered response, not implementation details, in PR #3058 tests
Two CodeRabbit nitpicks from the second review round: the uncategorized-count
cache-reuse assertion matched a scope name that never appears in generated SQL
(making it vacuous), and the recurring-cache revocation test read the
controller's private @projected_recurring ivar instead of the rendered page.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix: bust transactions-index caches on entry/recurring deletion and account status change
jjmata's PR review flagged two invalidation gaps: hard-deleting an
uncategorized entry or recurring transaction left the previous max
updated_at unchanged (cache never busted), and toggling an account's
active status doesn't touch entries/AccountShare at all. Fold in
counts (like account_share_version already did) and a new
Family#accounts_status_version, and move the version helpers onto
Family/Current per the "fat models, skinny controllers" nit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix: include merchant version in projected_recurring cache key
Editing or deleting a FamilyMerchant doesn't touch recurring_transactions,
so the cached projected-recurring list (rendered with merchant name/logo,
expires_in: 1.day) could show stale merchant data for up to a day.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(transactions): version projected-recurring cache by referenced merchants, not just FamilyMerchant
recurring_transactions.merchant_id can point at a shared ProviderMerchant
(recurring detection copies transaction.merchant_id), not just a
family-owned FamilyMerchant. merchants_version only tracked
Family#merchants (FamilyMerchant), so a ProviderMerchant update (e.g.
ProviderMerchant::Enhancer setting name/logo) left the cached projected
recurring list stale for up to a day.
Replace Family#merchants_version with #recurring_transaction_merchants_version,
scoped to the merchant records actually referenced by the family's recurring
transactions (both FamilyMerchant and ProviderMerchant), and bump the cache
key version. Also fix a flaky test assertion that matched all
recurring_transactions-table queries instead of the actual projection query,
since computing the cache key itself still runs small COUNT/MAX queries
against that table on a cache hit.
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Gerald <248542187+gfr-free@users.noreply.github.com>
* fix Prism issue - assigned but unused variable plaid_item
* measurement to improve accounts_controller__index performance based on skylight
* fix FEEDBACK - Stop eager-loading full sync histories on accounts index
* fix FEEDBACK - Remove sticky memoization from SimplefinItem#accounts
* fix lint error in PR review
* fix FEEDBACK - Avoid returning map misses as authoritative results
* fix test error
* fix FEEDBACK - Avoid redundant query by using already-loaded @manual_accounts
* Address maintainer review on accounts index sync preloading
Memoize SimplefinItem#accounts, align syncing? with Sync#visible?,
and add fallback regression tests.
* Add composite index for sync DISTINCT ON queries
Support latest_by_syncable ordering with syncable_type, syncable_id,
created_at DESC, and id DESC.
* fix error in dockerfile.preview file
* Suppress Pipelock false positives on CI database fixtures.
Pipelock 2.7.0 full-repo audit flags ephemeral postgres:// URLs in
workflow env blocks. Re-apply inline suppressions lost in the main merge.
* Address review: guard partial Current sync maps and drop unrelated diff
Add key? checks so partially populated Current sync maps fall back to DB
queries. Revert Dockerfile.preview and pipelock.yml changes unrelated to
the accounts index N+1 optimization.
* fix(sync): fully populate Current sync maps for all preloaded syncables
* fix(ci): skip scheduled preview cleanup on forks
Only run the hourly Cloudflare preview cleanup on we-promise/sure,
where the required secrets exist.
* Remove schema.rb Postgres-version churn, keep only new syncs index
Reset db/schema.rb to upstream/main and re-add only the
index_syncs_on_syncable_and_created_at_and_id index. The prior diff
included ~30 lines of noise (check-constraint reformatting, virtual
column reformat, and column reorderings) caused by dumping under a
different PostgreSQL version, which obscured the single intended
schema change and invited merge conflicts.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>