Keep rollouts and the merges they answer in the rollouts bucket (issue 1092)
Once chain_moved records the kind, a plan is a key of the rollouts bucket under its id and the open batch is the same key in state assembling. A save refuses a copy another save moved since (the plan's own Revision, kept in its value, counts saves) and keeps the merges the record holds, so a copy read before a merge moved cannot lay it back. The merges a batch or walk answers are the record's held field, with the columns the table had; a merge no plan answers stays, flagged, in the record that held it last, or in the record kept for those. A merge moved to a plan is written to the receiving record first and then taken from the giving one; one found in both is read from the one it entered last and repaired at the next cut. Plans and merges are read from a view kept by one watch of the bucket. A bus that does not answer, a lease lost and a key moved are now not-now like the store being away, so a merge whose plan could not be written is held and handed over again, never acknowledged. The start moves release_plan and batched_merge into the bucket, one kind, counted against both tables.
This commit is contained in:
@@ -10,6 +10,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/chain"
|
||||
)
|
||||
|
||||
// A Plan is what a merge produces (novox/hq ADR 0162): the modules it changed and everything
|
||||
@@ -66,6 +68,15 @@ type Plan struct {
|
||||
// per machine and module, what was asked and how it went, and the machines with no backup holder. Nil for a
|
||||
// walk that moves no module with an item protected by a backup, and for a walk kept before it was.
|
||||
Backups *PlanBackups `json:"backups,omitempty"`
|
||||
// Links are what the rollout names of the chain (novox/hq ADR 0408 §1): the builds it carried, the machine steps it has
|
||||
// taken and the assignments it changed, each with the build before and after. Kept in the rollout's record beside
|
||||
// the fields above; empty in the controller's store.
|
||||
chain.Links
|
||||
// Held are the merges this plan or batch holds (novox/hq ADR 0276, ADR 0408): what the store kept as batched merges
|
||||
// with this plan's id, and the merges no plan answers that it held last. **Written by the merge operations only**: a
|
||||
// save of the plan keeps what the record holds, never what this copy read, so a copy read before a merge moved cannot
|
||||
// lay it back (issue 1092).
|
||||
Held []chain.HeldMerge `json:"held,omitempty"`
|
||||
// WalkSaid is, on the save that started or ended the walk, which of the two it was (novox/hq ADR 0308
|
||||
// decision 1): set by the save, never kept, read by whoever says the save (`plan-moved`). Empty on every
|
||||
// other save.
|
||||
@@ -670,6 +681,11 @@ type MergeMove struct {
|
||||
// somebody else since it was read refuses the whole act with ErrPlanMoved. Each plan is said (PlanSaved) once
|
||||
// the transaction is committed, in order.
|
||||
func (i *Inventory) SavePlansMoving(ctx context.Context, plans []*Plan, moves []MergeMove) (err error) {
|
||||
if c, on, cerr := i.ChainReads(ctx, KindRollouts); cerr != nil {
|
||||
return cerr
|
||||
} else if on {
|
||||
return i.chainSavePlans(ctx, c, plans, moves)
|
||||
}
|
||||
epoch, err := i.actingEpoch(ctx)
|
||||
if err != nil {
|
||||
if len(plans) > 0 {
|
||||
@@ -818,16 +834,35 @@ func mergedAt(t time.Time) *time.Time {
|
||||
|
||||
// OpenPlans is every plan still being worked, oldest first.
|
||||
func (i *Inventory) OpenPlans(ctx context.Context) ([]Plan, error) {
|
||||
if c, on, err := i.ChainReads(ctx, KindRollouts); err != nil {
|
||||
return nil, err
|
||||
} else if on {
|
||||
return i.chainPlans(ctx, c, func(e viewEntry) bool { return e.sum.open() }, sortCreated)
|
||||
}
|
||||
return i.plans(ctx, `where state in ('building', 'rolling') order by created`)
|
||||
}
|
||||
|
||||
// PlansSince is every plan made after a moment, and every plan still being worked, oldest first.
|
||||
func (i *Inventory) PlansSince(ctx context.Context, since time.Time) ([]Plan, error) {
|
||||
if c, on, err := i.ChainReads(ctx, KindRollouts); err != nil {
|
||||
return nil, err
|
||||
} else if on {
|
||||
return i.chainPlans(ctx, c, func(e viewEntry) bool { return e.sum.Created.After(since) || e.sum.open() }, sortCreated)
|
||||
}
|
||||
return i.plans(ctx, `where created > $1 or state in ('building', 'rolling') order by created`, since)
|
||||
}
|
||||
|
||||
// RecentPlans is the last few plans, newest first, open or not — what the overview shows.
|
||||
func (i *Inventory) RecentPlans(ctx context.Context, limit int) ([]Plan, error) {
|
||||
if c, on, err := i.ChainReads(ctx, KindRollouts); err != nil {
|
||||
return nil, err
|
||||
} else if on {
|
||||
plans, err := i.chainPlans(ctx, c, func(viewEntry) bool { return true }, sortCreatedNewest)
|
||||
if err == nil && limit >= 0 && len(plans) > limit {
|
||||
plans = plans[:limit]
|
||||
}
|
||||
return plans, err
|
||||
}
|
||||
return i.plans(ctx, `order by created desc limit $1`, limit)
|
||||
}
|
||||
|
||||
@@ -835,6 +870,11 @@ func (i *Inventory) RecentPlans(ctx context.Context, limit int) ([]Plan, error)
|
||||
// planned: the branch's newest commit the mesh knows of (novox/hq issue 349). False when no plan of it
|
||||
// recorded when its merge was made.
|
||||
func (i *Inventory) NewestMergeOf(ctx context.Context, repository, branch string) (Plan, bool, error) {
|
||||
if c, on, err := i.ChainReads(ctx, KindRollouts); err != nil {
|
||||
return Plan{}, false, err
|
||||
} else if on {
|
||||
return i.chainNewestMergeOf(ctx, c, repository, branch)
|
||||
}
|
||||
plans, err := i.plans(ctx, `where lower(repository) = lower($1) and branch = $2 and merged_at is not null
|
||||
and release is null order by merged_at desc, created desc limit 1`, repository, branch)
|
||||
if err != nil || len(plans) == 0 {
|
||||
@@ -846,6 +886,11 @@ func (i *Inventory) NewestMergeOf(ctx context.Context, repository, branch string
|
||||
// PlanByID is one plan. The id is a parameter, never part of the query (novox/hq issue 561): it comes from a
|
||||
// caller of the plans verb.
|
||||
func (i *Inventory) PlanByID(ctx context.Context, id string) (Plan, error) {
|
||||
if c, on, err := i.ChainReads(ctx, KindRollouts); err != nil {
|
||||
return Plan{}, err
|
||||
} else if on {
|
||||
return i.chainPlanByID(ctx, c, id)
|
||||
}
|
||||
plans, err := i.plans(ctx, `where id = $1`, id)
|
||||
if err != nil {
|
||||
return Plan{}, err
|
||||
|
||||
Reference in New Issue
Block a user