-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgeneration.go
More file actions
101 lines (88 loc) · 3.62 KB
/
Copy pathgeneration.go
File metadata and controls
101 lines (88 loc) · 3.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package qi
import (
"encoding/json"
"errors"
"fmt"
"github.com/iamseth/yin"
)
const (
// DefaultDocumentID identifies the single document used by the demo CLI.
DefaultDocumentID = "default"
// InitialGeneration is the first checkpoint generation.
InitialGeneration uint64 = 1
checkpointFormatVersion = 1
)
var (
// ErrDocumentMismatch means an artifact was routed to a different document.
ErrDocumentMismatch = errors.New("qi: document mismatch")
// ErrGenerationMismatch means an artifact belongs to another checkpoint generation.
ErrGenerationMismatch = errors.New("qi: generation mismatch")
// ErrMalformedSyncArtifact distinguishes invalid artifact or yin bytes from identity mismatches.
ErrMalformedSyncArtifact = errors.New("qi: malformed sync artifact")
)
// DocumentIdentity is the compatibility boundary for one synchronized document.
// DocumentID is stable for the document's lifetime; Generation increases on
// checkpoint rotation and is never inferred from yin causal versions.
type DocumentIdentity struct {
DocumentID string `json:"document_id"`
Generation uint64 `json:"generation"`
}
// Cursor is generation-scoped causal coverage. A version vector without this
// identity must never be used for extraction in another generation.
type Cursor struct {
Identity DocumentIdentity `json:"identity"`
Version yin.VersionVector `json:"version"`
}
// DeltaArtifact associates encoded yin delta bytes with their document generation.
type DeltaArtifact struct {
Identity DocumentIdentity `json:"identity"`
Payload []byte `json:"payload"`
}
// SnapshotArtifact associates a durable yin snapshot with its document generation.
type SnapshotArtifact struct {
FormatVersion int `json:"format_version"`
Identity DocumentIdentity `json:"identity"`
Payload []byte `json:"payload"`
}
// CatchUp contains at most one generation-sensitive artifact. A snapshot means
// the requester must bootstrap; a delta is normal same-generation catch-up.
type CatchUp struct {
Snapshot *SnapshotArtifact `json:"snapshot,omitempty"`
Delta *DeltaArtifact `json:"delta,omitempty"`
}
func validateIdentity(identity DocumentIdentity) error {
if identity.DocumentID == "" {
return fmt.Errorf("%w: empty document id", ErrMalformedSyncArtifact)
}
if identity.Generation == 0 {
return fmt.Errorf("%w: zero generation", ErrMalformedSyncArtifact)
}
return nil
}
// encodeDelta encodes a yin delta with qi's canonical JSON LWW-map codec. It
// is the single site that selects the typed generic codec for encoding.
func encodeDelta(delta yin.Delta) ([]byte, error) {
return yin.NewJSONLWWMapDeltaCodec[json.RawMessage]().EncodeDelta(delta)
}
// decodeDelta decodes bytes with qi's canonical JSON LWW-map codec and wraps a
// decode failure as ErrMalformedSyncArtifact, the uniform class for a correctly
// routed artifact carrying invalid yin bytes.
func decodeDelta(data []byte) (yin.Delta, error) {
delta, err := yin.NewJSONLWWMapDeltaCodec[json.RawMessage]().DecodeDelta(data)
if err != nil {
return nil, fmt.Errorf("%w: yin delta: %w", ErrMalformedSyncArtifact, err)
}
return delta, nil
}
func compareIdentity(current, incoming DocumentIdentity) error {
if err := validateIdentity(incoming); err != nil {
return err
}
if current.DocumentID != incoming.DocumentID {
return fmt.Errorf("%w: have %q, artifact has %q", ErrDocumentMismatch, current.DocumentID, incoming.DocumentID)
}
if current.Generation != incoming.Generation {
return fmt.Errorf("%w: have %s generation %d, artifact has generation %d", ErrGenerationMismatch, current.DocumentID, current.Generation, incoming.Generation)
}
return nil
}