fix(autoscaling,ec2,kinesis): persist CFN ASG + scheduler/pipes deliveries and guard EC2 restart reconcile (bug-hunt)#2316
Merged
Conversation
…eries and guard EC2 restart reconcile (bug-hunt)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a cluster of four CONFIRMED restart data-loss / persistence bugs that share one root cause: a write path mutates in-memory state through a service built WITHOUT a snapshot store, so it never persists and never reconciles on restart.
CFN Auto Scaling Group loses ALL instances on restart.
CreateStack/UpdateStackinsert the ASG withinstances=[], firecfn_reconcile_capacityDETACHED, thenpersist_touched_servicesserializes the group empty. The detached reconcile pushed launched instances intog.instancesbut never re-persisted, so the ASG reloaded with zero instances. The detached reconcile now fires the autoscaling snapshot hook after it finishes mutating, so the launched instances survive restart. Threaded the autoscaling + EC2 snapshot hooks throughContainerBackingHandles::with_snapshot_hooksat every stack path (CreateStack, UpdateStack, ExecuteChangeSet, Cloud Control).ASG-launched EC2 instances had no snapshot store -> container leak on restart.
run_ec2_instancesdrives a bareEc2Service::with_state(...)(no snapshot store), so the EC2 records lived only in memory; on restart EC2 boot-recovery had no persistedpending/runningrow to re-drive and the containers leaked with no owning state.apply_capacitynow fires an EC2 snapshot hook after launching/terminating instances. Wired for BOTH the CFN reconcile path and the direct-API path (autoscaling_service.with_ec2_snapshot_hook(...)in the server), and fired before the ASG write lock so a concurrent group delete can't skip it.EC2
recover_persisted_containersincomplete reconcile guard (restart lifecycle race). During the restart recovery window the Ok arm guarded onlystate_code == 48and the Err arm guarded neither terminal code, so a concurrentStopInstances(code 80) was clobbered back torunningwith a fresh container, and a concurrentTerminateInstances(code 48) on a boot failure was overwritten tostopped. Both arms now use the same both-terminal guard (recovery_terminal_wins) that the normal-pathreconcile_starteduses, so a concurrent stop/terminate wins.Scheduler/Pipes Kinesis delivery never persisted. The EventBridge Scheduler and EventBridge-Pipes delivery buses built their Kinesis sender with
KinesisDeliveryImpl::new(...)(which setsdirty: None, makingmark_dirty()a no-op), so records delivered to a Kinesis stream target by a schedule or a pipe landed in memory but never flipped the sharedkinesis_delivery_dirtyflag the flusher persists on. Both sites now usewith_dirty_flag(kinesis_state.clone(), kinesis_delivery_dirty.clone()), matching the main EventBridge-rule path.Test plan
cargo test -p fakecloud-autoscaling -p fakecloud-ec2 -p fakecloud-kinesis— all pass, including new regressions:cfn_provision::tests::cfn_reconcile_persists_asg_and_ec2_instances— runs the CFN ASG reconcile with capturing snapshot stores and asserts the autoscaling snapshot (ci: run examples in CI #1) AND the EC2 snapshot (ci: add Moto compatibility test suite to CI #2) both contain the launched instances after reconcile.service::recover_guard_tests::*— assert the recovery reconcile does NOT overwrite a concurrent terminate (48) / stop (80) in either the Ok or Err arm, and still recovers a still-pending instance.delivery::tests::new_does_not_mark_dirty_but_with_dirty_flag_does— pins the::new(no-op flag) vswith_dirty_flag(flips the shared flag) contract the scheduler/pipes wiring depends on.cargo build -p fakecloud --tests— server-bin wiring compiles.cargo clippy -p fakecloud-autoscaling -p fakecloud-ec2 -p fakecloud-kinesis -p fakecloud-cloudformation --all-targets -- -D warningsandcargo clippy -p fakecloud --bin fakecloud -- -D warnings— clean.cargo fmt— clean.cargo test -p fakecloud-cloudformation— 270 pass (no regression from theContainerBackingHandlesplumbing).No Docker is required by the new tests; they assert the persist/serialize path is invoked with capturing snapshot stores and metadata-only EC2 launches.
Surface sync
Internal persistence/reconcile correctness only. No new API surface, field, operation, or response shape; no behavior visible to clients changed. No SDK / docs / website / metadata change is needed.
Summary by cubic
Fixes four restart data-loss bugs by persisting ASG/EC2 state and Kinesis deliveries, and by guarding EC2 restart recovery. ASG instances and scheduler/pipe records now survive restarts, and EC2 state is no longer clobbered during recovery.
ContainerBackingHandles.with_snapshot_hooksand passed tocfn_reconcile_capacity.apply_capacitytriggers an EC2 snapshot after launch/terminate; server wireswith_ec2_snapshot_hook(...), and the hook runs before the ASG write to avoid a delete race.recovery_terminal_winsguard (codes 48/80) so concurrent Stop/Terminate wins and is not overwritten.with_dirty_flag(kinesis_state, kinesis_delivery_dirty)instead of::newso the background flusher persists them.Written for commit 05886de. Summary will update on new commits.