mesh/merge-gate head 68db18bdfc1b merged into trunk da6d038e07c0 — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head 68db18bdfc1b merged into trunk da6d038e07c0 — pass: its merge-check.sh passed
mesh/delivery delivered
The merge gate raises the snapshot's mesh in a throwaway store and now gives it a chain in memory, since the chain lives only on the bus. A module with no commit is carried by no send (a send is read from the commits on the assignments' keys), so the grants step flags only a module with a commit as newly assigned. The tests that sent a module with no build record one, the merge test waits for the merges' consumer before publishing and for the last look of a window before the next test, the pending test follows a chain that deletes nothing, and a wait's settling is a value of its key.
116 lines
3.9 KiB
Go
116 lines
3.9 KiB
Go
package inventory
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/novox/mesh-controller/internal/chain"
|
|
)
|
|
|
|
// **The chain lives on the bus** (novox/hq ADR 0408, design 47 milestone 5): builds, rollouts and assignments are keys of the
|
|
// bus's key-value store, the controller's store keeps none of them, and every method of a kind reads and writes the chain this
|
|
// inventory is given (theChain).
|
|
|
|
// testChains are the chains in memory a test made for its store, by the store's address: every inventory opened on that
|
|
// store is given the same chain, as every process of a mesh is on the same bus. Empty outside a test.
|
|
var testChains sync.Map
|
|
|
|
// ErrNoChain is a read or write of the chain by an inventory that has none attached: a process that did not open the bus.
|
|
// Refused loudly.
|
|
var ErrNoChain = errors.New("this kind of record lives on the bus, and this process has not opened the bus")
|
|
|
|
// chainState is what an inventory keeps of the chain it is given.
|
|
type chainState struct {
|
|
mu sync.Mutex
|
|
open ChainOpener
|
|
// view is the rollouts bucket kept in memory by one watch (issue 1092); pending are the merges added to a plan that
|
|
// is not saved yet.
|
|
view *rolloutView
|
|
pending map[string]pendingMerge
|
|
// requireLayout is set for a chain on the bus: its builds are read only once the controller's start has laid them out
|
|
// under their modules (issue 1121), and laid is that seen.
|
|
requireLayout, laid bool
|
|
}
|
|
|
|
// RequireLayout makes every read of the chain refuse (ErrNotLaidOut) until the builds are laid out under their modules.
|
|
func (i *Inventory) RequireLayout() {
|
|
i.state.mu.Lock()
|
|
i.state.requireLayout = true
|
|
i.state.mu.Unlock()
|
|
}
|
|
|
|
func (i *Inventory) markLaidOut() {
|
|
i.state.mu.Lock()
|
|
i.state.laid = true
|
|
i.state.mu.Unlock()
|
|
}
|
|
|
|
// ChainOpener opens the chain on the bus when a process first needs it: a command that reads and never moved anything has
|
|
// no bus connection until a kind it reads turns out to be moved.
|
|
type ChainOpener func(ctx context.Context) (*chain.Chain, error)
|
|
|
|
// UseChainOpener gives the inventory the means to open the chain when it needs it and none is attached.
|
|
func (i *Inventory) UseChainOpener(open ChainOpener) {
|
|
i.state.mu.Lock()
|
|
i.state.open = open
|
|
i.state.requireLayout = true
|
|
i.state.mu.Unlock()
|
|
}
|
|
|
|
// UseChain attaches the chain the moved kinds are read and written through. The serving controller attaches the bus's,
|
|
// holding the lease for its writes; a test attaches one in memory.
|
|
func (i *Inventory) UseChain(c *chain.Chain) {
|
|
i.state.mu.Lock()
|
|
defer i.state.mu.Unlock()
|
|
i.chain = c
|
|
if i.state.view != nil {
|
|
i.state.view.stop()
|
|
i.state.view = nil
|
|
}
|
|
}
|
|
|
|
// UseThrowawayChain gives the inventory a chain in memory that is its own, laid out already: for a mesh raised for a moment
|
|
// beside the real one, as the merge gate raises the facts snapshot's mesh in a throwaway store (issue 1121). Nothing of it
|
|
// reaches the bus.
|
|
func (i *Inventory) UseThrowawayChain() {
|
|
i.UseChain(chain.InMemory(nil))
|
|
i.markLaidOut()
|
|
}
|
|
|
|
// attached is the chain this inventory has, nil when none.
|
|
func (i *Inventory) attached() *chain.Chain {
|
|
i.state.mu.Lock()
|
|
defer i.state.mu.Unlock()
|
|
return i.chain
|
|
}
|
|
|
|
// theChain is the chain this inventory writes and reads, opened if a command has not yet. On the bus it is refused until the
|
|
// builds are laid out under their modules.
|
|
func (i *Inventory) theChain(ctx context.Context) (*chain.Chain, error) {
|
|
i.state.mu.Lock()
|
|
if i.chain == nil && i.state.open != nil {
|
|
c, err := i.state.open(ctx)
|
|
if err != nil {
|
|
i.state.mu.Unlock()
|
|
return nil, fmt.Errorf("%w: %v", ErrNoChain, err)
|
|
}
|
|
i.chain = c
|
|
}
|
|
c, check := i.chain, i.state.requireLayout && !i.state.laid
|
|
i.state.mu.Unlock()
|
|
if c == nil {
|
|
return nil, ErrNoChain
|
|
}
|
|
if check {
|
|
if _, found, err := c.Builds.GetRaw(ctx, chain.LayoutMarkerKey); err != nil {
|
|
return nil, err
|
|
} else if !found {
|
|
return nil, ErrNotLaidOut
|
|
}
|
|
i.markLaidOut()
|
|
}
|
|
return c, nil
|
|
}
|