Retire a consumer the mesh stops asking for, and delete only on a person's word (hq ADR 0230)
The hourly release of ADR 0229's brake still ended in the mesh acting alone on a mistake. A consumer now stays active until the same unasked set holds for five passes, waits for a person past three or half of those held, is disabled and marked rather than withdrawn, comes back as it was when asked again, and is deleted only through the provider's delete tool. The backend keeps the mark, so a restart forgets nothing and finds what was withdrawn before.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -10,34 +9,6 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type recorder struct {
|
||||
created []Provision
|
||||
removed []string
|
||||
held bool
|
||||
failing error
|
||||
holdsErr error
|
||||
}
|
||||
|
||||
func (r *recorder) Create(_ context.Context, p Provision) error {
|
||||
if r.failing != nil {
|
||||
return r.failing
|
||||
}
|
||||
r.created = append(r.created, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *recorder) Remove(_ context.Context, as string, _ map[string]any) error {
|
||||
r.removed = append(r.removed, as)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *recorder) Holds(context.Context, Provision) (bool, error) {
|
||||
if r.holdsErr != nil {
|
||||
return false, r.holdsErr
|
||||
}
|
||||
return r.held, nil
|
||||
}
|
||||
|
||||
type world struct {
|
||||
t *testing.T
|
||||
dir string
|
||||
@@ -102,18 +73,18 @@ func TestAddingExtensionsAppliesTheConsumerAgain(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAConsumerNoLongerAskedForIsWithdrawn(t *testing.T) {
|
||||
func TestAConsumerNoLongerAskedForIsRetiredOnceStable(t *testing.T) {
|
||||
w := newWorld(t)
|
||||
w.give(map[string]any{"as": "a"}, map[string]any{"as": "b"})
|
||||
w.h.Reconcile(ctx)
|
||||
w.give(map[string]any{"as": "a"})
|
||||
w.h.Reconcile(ctx)
|
||||
w.passes(25 * time.Second) // five passes
|
||||
if strings.Join(w.a.removed, ",") != "b" {
|
||||
t.Fatal(w.a.removed)
|
||||
}
|
||||
// Only a file that says nobody asks withdraws everybody.
|
||||
// Only a file that says nobody asks retires the last one.
|
||||
w.give()
|
||||
w.h.Reconcile(ctx)
|
||||
w.passes(25 * time.Second)
|
||||
if strings.Join(w.a.removed, ",") != "b,a" {
|
||||
t.Fatal(w.a.removed)
|
||||
}
|
||||
@@ -137,7 +108,7 @@ func TestNothingReadIsNotNobodyAsking(t *testing.T) {
|
||||
}
|
||||
w.h.Reconcile(ctx)
|
||||
if len(w.a.removed) != 0 {
|
||||
t.Fatalf("withdrew %v on a file it could not use", w.a.removed)
|
||||
t.Fatalf("retired %v on a file it could not use", w.a.removed)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -206,7 +177,9 @@ func TestProvisionerCreatesTheDatabaseThenItsExtensions(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sql := f.statements()
|
||||
if !strings.HasPrefix(sql[len(sql)-1], `CREATE EXTENSION IF NOT EXISTS "vector"`) || !has(sql, `CREATE DATABASE "mesh_ace_letta"`) {
|
||||
// The extension once the database exists, and last the active mark (novox/hq ADR 0230).
|
||||
if !strings.HasPrefix(sql[len(sql)-2], `CREATE EXTENSION IF NOT EXISTS "vector"`) || !has(sql, `CREATE DATABASE "mesh_ace_letta"`) ||
|
||||
sql[len(sql)-1] != `COMMENT ON ROLE "mesh_ace_letta" IS '{"mesh":"consumer"}'` {
|
||||
t.Fatal(strings.Join(sql, "\n"))
|
||||
}
|
||||
if strings.Join(events, ",") != "database.provisioned" {
|
||||
@@ -228,7 +201,7 @@ func TestProvisionerCreatesTheDatabaseThenItsExtensions(t *testing.T) {
|
||||
|
||||
f.reset()
|
||||
f.answer(`FROM pg_roles`, []string{"?column?"}, []string{"1"})
|
||||
if err := a.Remove(ctx, "mesh_ace_letta", nil); err != nil {
|
||||
if err := a.Retire(ctx, "mesh_ace_letta", nil, "test", time.Now()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
noDrop(t, f.statements())
|
||||
|
||||
Reference in New Issue
Block a user