Repository navigation
feat(components): enforce source ownership on observation and deactivation RPCs - #1238
VLukiantsev merged 10 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces cluster fence and source ownership enforcement for non-intent lifecycle mutations, specifically for the PrepareDeactivation, CompleteDeactivation, and UpdateObservedGeneration operations. It updates the protobuf definitions to include an optional RequestSource field, propagates this source from the bridge reconciler through the CMS server to the underlying memory and Spanner stores, and adds comprehensive tests to verify the ownership enforcement. There are no review comments to address, and the implementation looks solid.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces cluster fence and source ownership enforcement for non-intent mutations, specifically during component deactivation preparation, completion, and observed generation updates. It updates the protobuf definitions to include a RequestSource field in the corresponding requests, adapts the CMS client, bridge lifecycle, CMS server, and both Memory and Spanner store backends to validate and record ownership details, and adds comprehensive parity tests. Feedback on the changes suggests adding a defensive nil check for the owner parameter in writeOwnerColumnsTx to prevent potential nil pointer dereferences.
85a0218 to
91f3a1a
Compare
327c51e to
d60679f
Compare
d60679f to
92ac481
Compare
92ac481 to
ef043b2
Compare
ef043b2 to
fe79a2b
Compare
5d2b99e to
fbe322b
Compare
5ad765f to
b7a0210
Compare
b7a0210 to
04cdf75
Compare
475ca0e to
6bfce07
Compare
6bfce07 to
1925d33
Compare
1925d33 to
e70f232
Compare
1cb3d32 to
a7b52de
Compare
a7b52de to
1a4a84f
Compare
1a4a84f to
3c02b01
Compare
…ation RPCs Add RequestSource to UpdateObservedGenerationRequest, PrepareDeactivationRequest, and CompleteDeactivationRequest; enforce cluster fence and source ownership across MemoryStore and SpannerStore; and wire bridge observation and deactivation calls through callWithOwnership. Bug: 548436300 TAG=agy CONV=d833b0e6-4821-48f6-8576-776d53f55a5c
Make writeOwnerColumnsTx a no-op for a nil owner so future call sites cannot dereference a nil SourceOwner. Comment link: #1238 (comment) Bug: 548436300 TAG=agy CONV=d833b0e6-4821-48f6-8576-776d53f55a5c
The source ownership layer replaced isOwnerObject with the nil-safe isOlderGeneration method. Use it for the stale-generation check on observation and deactivation RPCs, and compare the owner UID directly when preserving the committed intent generation. Bug: 548436300 TAG=agy CONV=d833b0e6-4821-48f6-8576-776d53f55a5c
…d deactivation calls Observation and deactivation RPCs now carry the request source, so the fenced standby envtest's epochOverrideCMS must raise their source epoch too, or UpdateObservedGeneration keeps the taken-over component fenced. Bug: 548436300 TAG=agy CONV=d833b0e6-4821-48f6-8576-776d53f55a5c
Restacking onto the #1233 hardening made staleGenerationError SetComponentIntent-only. Per the amended R3, UpdateObservedGeneration, PrepareDeactivation and CompleteDeactivation from the owner object are always allowed at any CR generation, so informer lag never blocks an observation or deactivation: checkSourceMutation now applies only the ownership rules. The parity test asserts that an older owner generation is admitted and keeps the recorded intent generation. Bug: 548436300 TAG=agy CONV=d833b0e6-4821-48f6-8576-776d53f55a5c
…ip no-op heartbeats An adoption (R2 or R5) through UpdateObservedGeneration, PrepareDeactivation or CompleteDeactivation now records SourceGeneration = 0, so the adopter's next intent is never rejected as STALE_GENERATION. An UpdateObservedGeneration that changes no observed generation, state or status leaves UpdateTime, the etag and the snapshot alone in both stores; Spanner still writes the owner columns, refreshing SourceLastSeenTime of a sourced call. Bug: 548436300 TAG=agy CONV=d833b0e6-4821-48f6-8576-776d53f55a5c
…test file Bug: 548436300 TAG=agy CONV=d833b0e6-4821-48f6-8576-776d53f55a5c
…e-driven Hoist the fenced-cluster, other-namespace and other-UID cases shared by the UpdateObservedGeneration, PrepareDeactivation and CompleteDeactivation rejection loops in TestSourcedObservationAndDeactivation_Parity into one named table, run through assertRejectedBy as op/case subtests in the original order and against the same store state. Run the unsourced and sourced unchanged heartbeats in TestUpdateObservedGeneration_UnchangedHeartbeat_Parity as named subtests. Inputs and assertions are unchanged. Bug: 548436300 TAG=agy CONV=d833b0e6-4821-48f6-8576-776d53f55a5c
…ct extra sources - The PrepareDeactivationRequest.source proto comment and the README state the cascade contract: with force, dependents moved to DISABLING are changed on behalf of the target's owner, skip the ownership check and keep their recorded owner. - optionalRequestSource rejects more than one RequestSource instead of silently dropping the extras. - Tests: the cascade keeps a dependent owned by another CR (both backends, SourceLastSeenTime on Spanner), and the bridge projects a rejected UpdateObservedGeneration or CompleteDeactivation as SourceConflict and stops the pass. Comment links: #1238 (comment) #1238 (comment) #1238 (comment) #1238 (comment) #1238 (comment) Bug: 548436300 TAG=agy CONV=d833b0e6-4821-48f6-8576-776d53f55a5c
…lifecycle RPCs Replace the local prepareDeactivationOperation, completeDeactivationOperation and updateObservedGenerationOperation constants with the shared operation* constants in error_info.go, adding operationCompleteDeactivation and operationUpdateObservedGeneration there. Bug: 548436300 TAG=agy CONV=d833b0e6-4821-48f6-8576-776d53f55a5c
Summary
Adds
RequestSourcetoUpdateObservedGeneration,PrepareDeactivationandCompleteDeactivation, enforces the cluster fence and source ownership on them in both stores, and wires the bridge's observation and deactivation calls throughcallWithOwnership.Motivation & Context
After #1237 and #1274 only intents were ownership-checked. A stale or foreign writer could still move observed state or deactivate a component it does not own.
Key Changes
RequestSourceon the three request messages.SourceLastSeenTimeand keepsSourceGeneration; an R2/R5 adoption recordsSourceGeneration = 0so the adopter's next intent is never stale) inMemoryStoreandSpannerStore.UpdateObservedGenerationthat changes nothing (a heartbeat) keepsUpdateTime, the etag and the snapshot; Spanner still writes the owner columns, refreshingSourceLastSeenTimeof a sourced call.cascade: truedeactivation skip the caller's per-CR uid match and keep their recorded owner, because CMS changes them on behalf of the target's owner (stated in thePrepareDeactivationRequest.sourceproto comment and the README; covered byTestPrepareDeactivation_CascadeKeepsDependentOwneron both backends).RequestSourceper call; extra sources are rejected withInvalidArgument.UpdateObservedGenerationorCompleteDeactivationis projected asSourceConflictand stops the pass (TestReconcileComponent_LifecycleOwnershipRejections).Stack Position
Assumptions
main, andgit range-diffshows no behavioral change apart from the hardening commit, which the rejection scenario below does not exercise — if wrong, the checkpoint must be re-run before merge.Verification & Testing
bazel test //pkg/components/store:store_test //pkg/components/cms:cms_test //pkg/components/bridge:bridge_test //api:protos_breaking_test//test/e2e:components_lifecycle_testpassed with the whole stack applied.tools/analyze_diff_size.py): 295 prod / 481 test / 776 total / 2 doc lines.Live Deployment & Manual Testing
enable_components: true, Spanner-backed CMS), upgraded with the dev-environment upgrade script including Spanner migrations014/015.5/5,16/16pods Ready, baseline Component CRs Ready and Synced).main.git range-diffagainst the current branches shows only rebase-induced import path updates (internal/logging) and the removal of unrelatedMODULE.bazel.lockchurn from feat(components): send RequestSource from bridge and record status.componentGeneration #1237; no behavioral change. The later hardening commit (SourceGeneration = 0on non-intent adoption, no-op heartbeats keepUpdateTime) is covered by store unit tests on memory and the Spanner emulator, not by the live run.UpdateObservedGeneration,PrepareDeactivationandCompleteDeactivationonmt-cp3-recreatewith a non-owning uid all failed withSOURCE_INCARNATION_MISMATCH, leavingObservedGeneration=3andSourceLastSeenTimeunchanged.Related Issues / Bugs
Bug: 548436300
TAG=agy
CONV=d833b0e6-4821-48f6-8576-776d53f55a5c