diff --git a/app/models/sync.rb b/app/models/sync.rb index 826899ce..4d923f12 100644 --- a/app/models/sync.rb +++ b/app/models/sync.rb @@ -21,58 +21,17 @@ class Sync < ApplicationRecord begin syncable.sync_data(self, start_date: start_date) - unless has_pending_child_syncs? - complete! - Rails.logger.info("Sync completed, starting post-sync") - syncable.post_sync(self) - Rails.logger.info("Post-sync completed") - end + complete! + Rails.logger.info("Sync completed, starting post-sync") + syncable.post_sync(self) + Rails.logger.info("Post-sync completed") rescue StandardError => error fail! error, report_error: true - ensure - notify_parent_of_completion! if has_parent? - end - end - end - - def handle_child_completion_event - Sync.transaction do - # We need this to ensure 2 child syncs don't update the parent at the exact same time with different results - # and cause the sync to hang in "syncing" status indefinitely - self.lock! - - unless has_pending_child_syncs? - if has_failed_child_syncs? - fail!(Error.new("One or more child syncs failed")) - else - complete! - end - - # If this sync is both a child and a parent, we need to notify the parent of completion - notify_parent_of_completion! if has_parent? - - syncable.post_sync(self) end end end private - def has_pending_child_syncs? - children.where(status: [ :pending, :syncing ]).any? - end - - def has_failed_child_syncs? - children.where(status: :failed).any? - end - - def has_parent? - parent_id.present? - end - - def notify_parent_of_completion! - parent.handle_child_completion_event - end - def start! Rails.logger.info("Starting sync") update! status: :syncing diff --git a/test/models/sync_test.rb b/test/models/sync_test.rb index 6b246a1f..99019146 100644 --- a/test/models/sync_test.rb +++ b/test/models/sync_test.rb @@ -31,44 +31,4 @@ class SyncTest < ActiveSupport::TestCase assert_equal "failed", @sync.status assert_equal "test sync error", @sync.error end - - # Order is important here. Parent syncs must implement sync_data so that their own work - # is 100% complete *prior* to queueing up child syncs. - test "runs sync with child syncs" do - family = families(:dylan_family) - - parent = Sync.create!(syncable: family) - child1 = Sync.create!(syncable: family.accounts.first, parent: parent) - child2 = Sync.create!(syncable: family.accounts.second, parent: parent) - grandchild = Sync.create!(syncable: family.accounts.last, parent: child2) - - parent.syncable.expects(:sync_data).returns([]).once - child1.syncable.expects(:sync_data).returns([]).once - child2.syncable.expects(:sync_data).returns([]).once - grandchild.syncable.expects(:sync_data).returns([]).once - - assert_equal "pending", parent.status - assert_equal "pending", child1.status - assert_equal "pending", child2.status - assert_equal "pending", grandchild.status - - parent.perform - assert_equal "syncing", parent.reload.status - - child1.perform - assert_equal "completed", child1.reload.status - assert_equal "syncing", parent.reload.status - - child2.perform - assert_equal "syncing", child2.reload.status - assert_equal "completed", child1.reload.status - assert_equal "syncing", parent.reload.status - - # Will complete the parent and grandparent syncs - grandchild.perform - assert_equal "completed", grandchild.reload.status - assert_equal "completed", child1.reload.status - assert_equal "completed", child2.reload.status - assert_equal "completed", parent.reload.status - end end