The package seats kept incoming, transfers and findings with no read or drop tool (tools review F4). npm_incoming and go_incoming list a seat's open transfers, incoming's fill and the cap its binding carries, and drop one by hand. The same fault as distribution's (running review F2, its second instance): the transfers state's own lifetime dropped a transfer's key and left its chunks, and the drop otherwise waited for the next publish. transfers keeps for good; each open transfer has a key in the new state idle with its own time to live, whose end drops it. The holder counts against its binding's cap. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
106 lines
3.7 KiB
Go
106 lines
3.7 KiB
Go
package main
|
|
|
|
// The package seats' incoming, read and steered (design 51 *Publishing*; tools review F4): npm_incoming and
|
|
// go_incoming list a seat's open transfers, incoming's fill and the cap its binding carries, and drop one by hand.
|
|
// The holder counts against that cap, never a number of its own, and drops a transfer left with no chunk for 24 hours
|
|
// when its idle key ends (the bus's marker), never on a timer.
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
"git.novox.be/novox/mesh-sdk/go/busstate"
|
|
|
|
"gitea/internal/packageseats"
|
|
)
|
|
|
|
// incomingTool is a seat's <type>_incoming.
|
|
func incomingTool(typ, seat string, holder func() (*packageseats.Holder, error)) stdio.Tool {
|
|
str := func(args map[string]any, k string) string { s, _ := args[k].(string); return strings.TrimSpace(s) }
|
|
return stdio.Tool{
|
|
Name: typ + "_incoming",
|
|
Description: "The " + seat + " seat's open transfers in the forge module's object store incoming (each with its " +
|
|
"opener, its parts and when it last took a chunk), incoming's fill and the cap its binding carries; a " +
|
|
"transfer with no chunk for 24 hours is dropped on its own. With drop, drops one by hand, its chunks with " +
|
|
"it (a hand act, why required). (a)",
|
|
Input: map[string]any{"type": "object", "properties": map[string]any{
|
|
"drop": map[string]any{"type": "string", "description": "a transfer id (the caller's own name) to drop"},
|
|
"why": map[string]any{"type": "string", "description": "why it is dropped"},
|
|
}},
|
|
Run: func(args map[string]any) (any, error) {
|
|
h, err := holder()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if id := str(args, "drop"); id != "" {
|
|
if str(args, "why") == "" {
|
|
return nil, errors.New("a drop is a hand act: say why")
|
|
}
|
|
t, err := h.Drop(seat, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fmt.Fprintf(os.Stderr, "transfer %s of %s dropped by hand by %s: %s\n", id, seat, stdio.CallerOf(args), str(args, "why"))
|
|
return map[string]any{"dropped": t.ID}, nil
|
|
}
|
|
return h.IncomingOf(seat)
|
|
},
|
|
}
|
|
}
|
|
|
|
// incomingCap reads incoming's cap from its binding (the bus's bound of the object store), once it answers one.
|
|
func incomingCap() func() (uint64, error) {
|
|
var known atomic.Uint64
|
|
return func() (uint64, error) {
|
|
if c := known.Load(); c > 0 {
|
|
return c, nil
|
|
}
|
|
f, err := busstate.ObjectsOf(packageseats.IncomingObjects).Fill()
|
|
if err != nil {
|
|
return 0, fmt.Errorf("incoming's cap cannot be read from its binding: %w", err)
|
|
}
|
|
if f.MaxBytes == 0 {
|
|
return 0, errors.New("incoming's binding carries no cap")
|
|
}
|
|
known.Store(f.MaxBytes)
|
|
return f.MaxBytes, nil
|
|
}
|
|
}
|
|
|
|
// armIdle writes a transfer's idle key with its own time to live; one already there is the same bound, kept.
|
|
func armIdle(key string, ttl time.Duration) error {
|
|
if _, err := stdio.State(packageseats.IdleState).CreateTTL(key, map[string]any{"transfer": key}, ttl); err != nil && !errors.Is(err, stdio.ErrChanged) {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// followIdle drops a transfer when its idle key ends (packageseats.Holder.Expired): the bus's marker is the event.
|
|
func followIdle(holder func() (*packageseats.Holder, error)) {
|
|
<-stdio.Served()
|
|
stdio.State(packageseats.IdleState).Follow("", func(ch stdio.StateChange) error {
|
|
if ch.Op != stdio.OpExpire {
|
|
return nil
|
|
}
|
|
h, err := holder()
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "transfer %s's idle bound ended, but the holder cannot be made: %v\n", ch.Key, err)
|
|
return err
|
|
}
|
|
dropped, err := h.Expired(ch.Key)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return err
|
|
}
|
|
if dropped {
|
|
fmt.Fprintf(os.Stderr, "transfer %s had no chunk for %s: dropped with its chunks\n", ch.Key, packageseats.TransferIdle)
|
|
}
|
|
return nil
|
|
})
|
|
}
|