Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: ActiveRecord::RecordInvalid (MAYBE-RAILS-D5) #1736

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

revise-dev[bot]
Copy link

@revise-dev revise-dev bot commented Jan 29, 2025

The error "ActiveRecord::RecordInvalid: Validation failed: Outflow transaction has already been taken" occurs because the account syncing process is attempting to create duplicate transfers where a transaction is being reused in multiple transfers. This violates the uniqueness validations defined in the Transfer model.

The root cause is in the transfer_match_candidates method in account.rb. The original LEFT JOIN clause only checked if either the inflow_transaction_id OR outflow_transaction_id matched exactly, but didn't properly exclude cases where a transaction ID might be used in the opposite role in an existing transfer.

Key changes:

  1. Modified the LEFT JOIN clause to check both transaction IDs against both the inflow and outflow fields of existing transfers. This ensures that if a transaction is used in ANY existing transfer (whether as inflow or outflow), it won't be considered as a candidate for new transfers.

  2. The new JOIN clause uses IN clauses to check both candidate transaction IDs against both fields of existing transfers, providing complete coverage of all possible combinations.

  3. Added comprehensive test coverage to verify the fix, including edge cases where:

    • Multiple matching transactions exist
    • Transactions are already part of existing transfers
    • Transactions with matching amounts but different dates
    • Cross-account scenarios within the same family

This fix maintains the original intent of the auto-matching feature while preventing the uniqueness validation error by properly excluding any transaction that's already part of a transfer.

The test suite additions validate both the happy path (successful matches) and edge cases (preventing duplicate transfers), ensuring the fix is robust and maintainable.

Error Details

Summary:

ActiveRecord::RecordInvalid: Validation failed: Outflow transaction has already been taken (ActiveRecord::RecordInvalid)

Stacktrace:

active_record/validations.rb:87
active_record/validations.rb:54
active_record/transactions.rb:366
active_record/transactions.rb:418
active_record/connection_adapters/abstract/database_statements.rb:359
active_record/transactions.rb:414
active_record/connection_adapters/abstract/connection_pool.rb:415
active_record/connection_handling.rb:296
active_record/transactions.rb:410
active_record/transactions.rb:366
active_record/suppressor.rb:56
active_record/persistence.rb:55
app/models/account.rb:210
active_record/relation/delegation.rb:98
active_record/relation/delegation.rb:98
app/models/account.rb:206
active_record/connection_adapters/abstract/transaction.rb:616
active_support/concurrency/null_lock.rb:9
active_record/connection_adapters/abstract/transaction.rb:613
active_record/connection_adapters/abstract/database_statements.rb:361
active_record/transactions.rb:234
active_record/connection_adapters/abstract/connection_pool.rb:415
active_record/connection_handling.rb:296
active_record/transactions.rb:233
app/models/account.rb:205
app/models/account/syncer.rb:8
app/models/account.rb:95
app/models/plaid_item.rb:41
active_record/relation/delegation.rb:98
active_record/relation/delegation.rb:98
app/models/plaid_item.rb:40
app/models/sync.rb:12
app/jobs/sync_job.rb:5
active_job/execution.rb:68
active_support/callbacks.rb:121
i18n.rb:353
active_job/translation.rb:9
active_support/callbacks.rb:130
active_support/callbacks.rb:130
active_support/core_ext/time/zones.rb:65
active_job/timezones.rb:9
active_support/callbacks.rb:130
active_support/callbacks.rb:130
active_support/callbacks.rb:141
active_job/execution.rb:67
active_job/instrumentation.rb:32
active_job/execution.rb:51
active_job/instrumentation.rb:26
active_record/railties/job_runtime.rb:13
active_job/instrumentation.rb:40
active_support/notifications.rb:210
active_support/notifications/instrumenter.rb:58
sentry/rails/tracing.rb:56
active_support/notifications.rb:210
active_job/instrumentation.rb:39
active_record/railties/job_runtime.rb:11
active_job/instrumentation.rb:26
active_job/logging.rb:32
active_support/tagged_logging.rb:138
active_support/tagged_logging.rb:38
active_support/tagged_logging.rb:138
active_support/broadcast_logger.rb:241
active_job/logging.rb:39
active_job/logging.rb:32
sentry/rails/active_job.rb:11
sentry/rails/active_job.rb:43
sentry/hub.rb:59
sentry-ruby.rb:399
sentry/rails/active_job.rb:26
sentry/rails/active_job.rb:10
active_job/execution.rb:29
active_support/callbacks.rb:121
active_job/railtie.rb:79
active_support/reloader.rb:77
active_support/execution_wrapper.rb:87
active_support/reloader.rb:74
active_job/railtie.rb:78
active_support/callbacks.rb:130
active_support/callbacks.rb:130
active_support/callbacks.rb:141
active_job/execution.rb:27
/home/deploy/maybe-production/shared/bundle/ruby/3.4.0/gems/good_job-4.8.2/app/models/good_job/job.rb:629
active_support/notifications.rb:210
active_support/notifications/instrumenter.rb:58
sentry/rails/tracing.rb:56
active_support/notifications.rb:210
/home/deploy/maybe-production/shared/bundle/ruby/3.4.0/gems/good_job-4.8.2/app/models/good_job/job.rb:628
good_job/current_thread.rb:113
/home/deploy/maybe-production/shared/bundle/ruby/3.4.0/gems/good_job-4.8.2/app/models/good_job/job.rb:586
active_support/callbacks.rb:121
/home/deploy/maybe-production/shared/bundle/ruby/3.4.0/gems/good_job-4.8.2/app/models/good_job/batch.rb:80
/home/deploy/maybe-production/shared/bundle/ruby/3.4.0/gems/good_job-4.8.2/app/models/good_job/job.rb:771
active_support/callbacks.rb:130
active_support/callbacks.rb:141
/home/deploy/maybe-production/shared/bundle/ruby/3.4.0/gems/good_job-4.8.2/app/models/good_job/job.rb:580
/home/deploy/maybe-production/shared/bundle/ruby/3.4.0/gems/good_job-4.8.2/app/models/good_job/job.rb:322
/home/deploy/maybe-production/shared/bundle/ruby/3.4.0/gems/good_job-4.8.2/app/models/concerns/good_job/advisory_lockable.rb:180
active_record/relation.rb:1355
active_record/relation.rb:541
active_record/scoping/default.rb:51
/home/deploy/maybe-production/shared/bundle/ruby/3.4.0/gems/good_job-4.8.2/app/models/concerns/good_job/advisory_lockable.rb:180
active_record/relation/delegation.rb:78
active_record/relation.rb:1355
active_record/relation.rb:541
active_record/relation/delegation.rb:78
/home/deploy/maybe-production/shared/bundle/ruby/3.4.0/gems/good_job-4.8.2/app/models/good_job/job.rb:316
active_record/relation/delegation.rb:78
active_record/relation.rb:1355
active_record/relation.rb:541
active_record/relation/delegation.rb:78
good_job/job_performer.rb:35
good_job/capsule_tracker.rb:94
good_job/job_performer.rb:34
good_job/scheduler.rb:289
active_support/reloader.rb:77
active_support/execution_wrapper.rb:91
active_support/reloader.rb:74
good_job/scheduler.rb:288
concurrent/executor/safe_task_executor.rb:24
concurrent/synchronization/mutex_lockable_object.rb:48
concurrent/synchronization/mutex_lockable_object.rb:48
concurrent/synchronization/mutex_lockable_object.rb:48
concurrent/executor/safe_task_executor.rb:22
concurrent/ivar.rb:170
concurrent/scheduled_task.rb:298
concurrent/executor/timer_set.rb:98
concurrent/executor/ruby_thread_pool_executor.rb:359
concurrent/executor/ruby_thread_pool_executor.rb:350
:168
concurrent/executor/ruby_thread_pool_executor.rb:341
concurrent/executor/ruby_thread_pool_executor.rb:340
concurrent/executor/ruby_thread_pool_executor.rb:340

Tip

You can make revisions or ask questions of Revise.dev by using /revise in any comment or review!

  • /revise Add a comment above the method to explain why we're making this change.
  • /revise Why did you choose to make this change specifically?

Important

If something doesn’t look right, click to retry this interaction.

Quick links: View in SentryView in Revise

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.

0 participants