| 1 | package hostclaim |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "github.com/carlosframework/platform/internal/release" |
| 6 | ) |
| 7 | |
| 8 | // PendingMoveFenceLoss selects the frozen target writer until its first recovery admission. |
| 9 | func PendingMoveFenceLoss(c *Claim) (LossRecord, bool) { |
| 10 | ref, ok := ConsumedMoveTargetFence(c) |
| 11 | if !ok { |
| 12 | return LossRecord{}, false |
| 13 | } |
| 14 | for _, l := range c.EvidenceRefs { |
| 15 | if l.Op != c.Op.ID || l.Box != c.Op.Intent["target"] || l.Fence != ref || l.Session == 0 || c.Op.Journal["fence_admitted."+l.ID] != "" { |
| 16 | continue |
| 17 | } |
| 18 | region := c.Op.Intent["to"] |
| 19 | return LossRecord{ID: l.ID, Host: c.Host, OpID: l.Op, OpKind: l.Kind, OpPhase: l.Phase, HolderIncarnation: l.Incarnation, HolderStamp: l.Stamp, HolderSession: l.Session, Box: l.Box, Enrollment: l.Enrollment, Automatic: &l, WriteSources: []WriteSource{{Reason: "target", Box: l.Box, Session: l.Session, Generation: l.Generation, Region: region, Baseline: "replica:" + region + "/" + l.Generation}}}, true |
| 20 | } |
| 21 | return LossRecord{}, false |
| 22 | } |
| 23 | |
| 24 | // AdmitWriterWithFencedTargetLoss binds the frozen target maximum independently of restore. |
| 25 | func AdmitWriterWithFencedTargetLoss(ctx context.Context, s release.Store, o Observed, in Admission, sources []ValidatedWriteSource) (Observed, error) { |
| 26 | record, ok := PendingMoveFenceLoss(o.Claim) |
| 27 | if !ok || o.Claim.Op.Phase != PhaseProbing || len(sources) != 1 || in.Coverage != "" || in.Mode != "cold" || in.KeepStamp || in.PredecessorCertified || in.LossID != "" || len(in.AdditionalWritesLost) != 0 { |
| 28 | return Observed{}, ErrFence |
| 29 | } |
| 30 | want, got := record.WriteSources[0], sources[0] |
| 31 | if got.Reason != want.Reason || got.Box != want.Box || got.Session != want.Session || got.Generation != want.Generation || got.Region != want.Region || got.Baseline != want.Baseline || in.RemoteMaxTXID != got.RemoteMaxTXID { |
| 32 | return Observed{}, ErrFence |
| 33 | } |
| 34 | h := o.Claim.Holder |
| 35 | p := AdmissionRecord{Box: h.WriterBox, Session: h.Session, Generation: h.Generation} |
| 36 | if len(h.Admissions) > 0 { |
| 37 | p = h.Admissions[len(h.Admissions)-1] |
| 38 | } |
| 39 | if p.Box != want.Box || p.Session != want.Session || p.Generation != want.Generation { |
| 40 | return Observed{}, ErrFence |
| 41 | } |
| 42 | in.fenceVerified = true |
| 43 | return Update(ctx, s, o, func(c *Claim) error { |
| 44 | if err := AdmitWriter(in)(c); err != nil { |
| 45 | return err |
| 46 | } |
| 47 | setJournal(c, "fence_admitted."+record.ID, "true") |
| 48 | return nil |
| 49 | }) |
| 50 | } |
| 51 | |