tinyrelayfile
tinyrelay/tinygit/journal.go/

tinygit/journal.go raw
1package tinygit
2
3import (
4 "context"
5 "crypto/sha256"
6 "encoding/hex"
7 "encoding/json"
8 "errors"
9 "fmt"
10 "os"
11 "path/filepath"
12 "strings"
13)
14
15type journalRecord struct {
16 Repository string `json:"repository"`
17 EventID string `json:"event_id"`
18 Kind int `json:"kind"`
19 Refs map[string]string `json:"refs,omitempty"`
20 Head string `json:"head,omitempty"`
21 Committed bool `json:"committed"`
22}
23
24func (g *GitRelay) journalDir() string { return filepath.Join(g.root, ".tinyrelay", "journal") }
25
26func (g *GitRelay) journalPath(r journalRecord) string {
27 h := sha256.Sum256([]byte(r.Repository + "\x00" + r.EventID))
28 return filepath.Join(g.journalDir(), hex.EncodeToString(h[:])+".json")
29}
30
31func (g *GitRelay) writeJournal(repo Repository, r journalRecord) error {
32 if err := os.MkdirAll(g.journalDir(), 0700); err != nil {
33 return err
34 }
35 b, err := json.Marshal(r)
36 if err != nil {
37 return err
38 }
39 tmp := g.journalPath(r) + ".tmp"
40 if err := os.WriteFile(tmp, b, 0600); err != nil {
41 return err
42 }
43 if err := syncFile(tmp); err != nil {
44 return err
45 }
46 return os.Rename(tmp, g.journalPath(r))
47}
48
49func (g *GitRelay) commitJournal(repo Repository, r journalRecord) error {
50 if err := os.Remove(g.journalPath(r)); err != nil && !errors.Is(err, os.ErrNotExist) {
51 return err
52 }
53 return nil
54}
55
56func syncFile(path string) error {
57 f, err := os.OpenFile(path, os.O_RDONLY, 0)
58 if err != nil {
59 return err
60 }
61 defer f.Close()
62 return f.Sync()
63}
64
65// recoverJournals completes state publication after a process crash. A
66// journal is deliberately small and repository-local; the event store remains
67// authoritative for event durability, while the file is the Git authority.
68func (g *GitRelay) recoverJournals() error {
69 entries, err := os.ReadDir(g.journalDir())
70 if errors.Is(err, os.ErrNotExist) {
71 return nil
72 }
73 if err != nil {
74 return err
75 }
76 for _, entry := range entries {
77 if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".json") {
78 continue
79 }
80 b, err := os.ReadFile(filepath.Join(g.journalDir(), entry.Name()))
81 if err != nil {
82 return err
83 }
84 var jr journalRecord
85 if err := json.Unmarshal(b, &jr); err != nil {
86 return fmt.Errorf("git relay journal %s: %w", entry.Name(), err)
87 }
88 owner, id, ok := strings.Cut(jr.Repository, "\x00")
89 if !ok || owner == "" || id == "" {
90 return fmt.Errorf("git relay journal %s: invalid repository", entry.Name())
91 }
92 r := Repository{Owner: owner, Identifier: id, Refs: jr.Refs, Head: jr.Head}
93 if err := g.ensureRepo(r); err != nil {
94 return err
95 }
96 keepJournal := false
97 if jr.Kind == 30618 {
98 current, found, err := g.store.Latest(context.Background(), 30618, owner, id)
99 if err != nil {
100 return err
101 }
102 if !found || current.ID != jr.EventID {
103 if removeErr := os.Remove(filepath.Join(g.journalDir(), entry.Name())); removeErr != nil && !errors.Is(removeErr, os.ErrNotExist) {
104 return removeErr
105 }
106 continue
107 }
108 if g.stateObjectsPresent(context.Background(), r) {
109 err = g.writeState(r)
110 } else {
111 err = g.writePendingState(r)
112 keepJournal = true
113 }
114 if err != nil {
115 return err
116 }
117 g.mu.Lock()
118 g.pending[jr.EventID] = struct{}{}
119 g.mu.Unlock()
120 } else if jr.Kind == 1617 || jr.Kind == 1618 || jr.Kind == 1619 {
121 if !g.stateObjectsPresent(context.Background(), r) {
122 if err := g.writePendingState(r); err != nil {
123 return err
124 }
125 keepJournal = true
126 g.mu.Lock()
127 g.pending[jr.EventID] = struct{}{}
128 g.mu.Unlock()
129 }
130 }
131 if !keepJournal {
132 if err := os.Remove(filepath.Join(g.journalDir(), entry.Name())); err != nil && !errors.Is(err, os.ErrNotExist) {
133 return err
134 }
135 }
136 }
137 return nil
138}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs