ADR 0408 section 4 says no key of the chain is removed, a delete marker included. The Backend loses Remove and the Chain RemoveAssignment. An unassignment, and the end of a value only pulled in by another, write the value again with ended set, by compare-and-set; assigning it again writes a fresh standing value over it. A value with ended or a state is no assignment (Assignment.Stands) to the assignment lists, the joins, forgetting a module, seat holdings, the given values, the move and the assignment-changed event.
136 lines
3.4 KiB
Go
136 lines
3.4 KiB
Go
package chain
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/nats-io/nats.go"
|
|
"github.com/nats-io/nats.go/jetstream"
|
|
|
|
"github.com/novox/mesh-controller/internal/broker"
|
|
)
|
|
|
|
// OnTheBus opens the chain's three buckets on a connection. The controller asserts them at its start
|
|
// (broker.EnsureControllerBuckets), so one older than this has not, and the error says so.
|
|
func OnTheBus(ctx context.Context, conn *nats.Conn, holds Holds) (*Chain, error) {
|
|
api, err := jetstream.New(conn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
open := func(bucket string) (Backend, error) {
|
|
kv, err := api.KeyValue(ctx, bucket)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("the bucket %s is not on the bus — the controller asserts it at its start, so one "+
|
|
"older than this has not: %w", bucket, err)
|
|
}
|
|
return busBucket{kv}, nil
|
|
}
|
|
builds, err := open(broker.BuildsBucket)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rollouts, err := open(broker.RolloutsBucket)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
assignments, err := open(broker.AssignmentsBucket)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return New(builds, rollouts, assignments, holds), nil
|
|
}
|
|
|
|
// busBucket is a Backend on a key-value bucket. It calls no delete and no purge: the type has none to call (ADR 0408 §4).
|
|
type busBucket struct{ kv jetstream.KeyValue }
|
|
|
|
func entryOf(e jetstream.KeyValueEntry) Entry {
|
|
return Entry{Key: e.Key(), Value: e.Value(), Revision: e.Revision(), At: e.Created()}
|
|
}
|
|
|
|
func (b busBucket) Get(ctx context.Context, key string) (Entry, bool, error) {
|
|
e, err := b.kv.Get(ctx, key)
|
|
if errors.Is(err, jetstream.ErrKeyNotFound) {
|
|
return Entry{}, false, nil
|
|
}
|
|
if err != nil {
|
|
return Entry{}, false, err
|
|
}
|
|
return entryOf(e), true, nil
|
|
}
|
|
|
|
func (b busBucket) Create(ctx context.Context, key string, value []byte) (uint64, error) {
|
|
rev, err := b.kv.Create(ctx, key, value)
|
|
if errors.Is(err, jetstream.ErrKeyExists) {
|
|
return 0, ErrExists
|
|
}
|
|
return rev, err
|
|
}
|
|
|
|
func (b busBucket) Update(ctx context.Context, key string, value []byte, revision uint64) (uint64, error) {
|
|
rev, err := b.kv.Update(ctx, key, value, revision)
|
|
var apiErr *jetstream.APIError
|
|
if errors.As(err, &apiErr) && apiErr.ErrorCode == jetstream.JSErrCodeStreamWrongLastSequence {
|
|
return 0, ErrMoved
|
|
}
|
|
return rev, err
|
|
}
|
|
|
|
func (b busBucket) Keys(ctx context.Context) ([]string, error) {
|
|
keys, err := b.kv.Keys(ctx)
|
|
if errors.Is(err, jetstream.ErrNoKeysFound) {
|
|
return nil, nil
|
|
}
|
|
return keys, err
|
|
}
|
|
|
|
func (b busBucket) Watch(ctx context.Context) (<-chan Entry, error) {
|
|
w, err := b.kv.WatchAll(ctx, jetstream.IgnoreDeletes())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make(chan Entry)
|
|
go func() {
|
|
defer close(out)
|
|
defer func() { _ = w.Stop() }()
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case e, ok := <-w.Updates():
|
|
if !ok {
|
|
return
|
|
}
|
|
v := Entry{Caught: true}
|
|
if e != nil {
|
|
v = entryOf(e)
|
|
}
|
|
select {
|
|
case out <- v:
|
|
case <-ctx.Done():
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
return out, nil
|
|
}
|
|
|
|
func (b busBucket) History(ctx context.Context, key string) ([]Entry, error) {
|
|
all, err := b.kv.History(ctx, key)
|
|
if errors.Is(err, jetstream.ErrKeyNotFound) {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]Entry, 0, len(all))
|
|
for _, e := range all {
|
|
if e.Operation() != jetstream.KeyValuePut {
|
|
continue // a marker an earlier mirror left; nothing of the chain makes one
|
|
}
|
|
out = append(out, entryOf(e))
|
|
}
|
|
return out, nil
|
|
}
|