zfs: read an unwritten ledger instance as state null and sequence 0, pass every error up (review finding 2, A-40)

Builds: design 42 zfs-owns-zfs A-40
This commit is contained in:
2026-11-13 01:28:51 +01:00
parent 008e02afbd
commit 5f372ac63a
2 changed files with 38 additions and 19 deletions
+17 -19
View File
@@ -162,15 +162,22 @@ func (busLedger) Apply(instance, operation string, body any, call string) error
return err
}
// absentInstance says whether a ledger get said the instance is not there, which an instance never written is.
func absentInstance(err error) bool {
s := strings.ToLower(err.Error())
for _, w := range []string{"not found", "no instance", "no such", "does not exist", "unknown instance", "not kept"} {
if strings.Contains(s, w) {
return true
}
// takenOf reads a ledger get's answer: an instance never written answers state null and sequence 0, which is empty (A-40).
func takenOf(raw []byte) (struct {
Taken map[string]LedgerEntry `json:"taken"`
}, error) {
var body struct {
Taken map[string]LedgerEntry `json:"taken"`
}
return false
var st buscalls.InstanceState
if err := json.Unmarshal(raw, &st); err != nil {
return body, err
}
if st.Sequence == 0 && (len(st.State) == 0 || string(st.State) == "null") {
return body, nil
}
err := json.Unmarshal(st.State, &body)
return body, err
}
func (busLedger) Entries(datasets []string) ([]LedgerEntry, error) {
@@ -179,19 +186,10 @@ func (busLedger) Entries(datasets []string) ([]LedgerEntry, error) {
for _, kind := range keepKinds {
raw, err := stdio.BusState(buscalls.LedgerGet, buscalls.InstanceRequest{Ledger: LedgerName, Instance: LedgerInstance(kind, ds)})
if err != nil {
if absentInstance(err) {
continue
}
return nil, err
}
var st buscalls.InstanceState
if err := json.Unmarshal(raw, &st); err != nil {
return nil, err
}
var body struct {
Taken map[string]LedgerEntry `json:"taken"`
}
if err := json.Unmarshal(st.State, &body); err != nil {
body, err := takenOf(raw)
if err != nil {
return nil, err
}
for _, name := range sortedKeys(body.Taken) {
@@ -0,0 +1,21 @@
package main
import "testing"
func TestTakenOfLedgerAnswers(t *testing.T) {
for _, c := range []struct {
name, raw string
want int
err bool
}{
{"never written", `{"instance":"x","state":null,"sequence":0}`, 0, false},
{"written", `{"instance":"x","state":{"taken":{"a":{}}},"sequence":3}`, 1, false},
{"bad state", `{"instance":"x","state":"oops","sequence":3}`, 0, true},
{"bad answer", `nope`, 0, true},
} {
b, err := takenOf([]byte(c.raw))
if (err != nil) != c.err || len(b.Taken) != c.want {
t.Errorf("%s: %v %d", c.name, err, len(b.Taken))
}
}
}