carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package registry
2
3import (
4 "database/sql"
5 "errors"
6)
7
8// MaterializeTimerRegistration preserves the identity already recorded by root.
9// The caller must retain host exclusion and check its inventory revision before
10// this write. Replay accepts only the exact resulting row, including ownership.
11func (r *Registry) MaterializeTimerRegistration(t Timer, expected string) (*Timer, error) {
12 if r.remote != nil {
13 out, err := r.remote.call(nil, remoteRequest{Method: "MaterializeTimerRegistration", T: t, Expected: expected}, nil)
14 return out.Timer, err
15 }
16 if !t.ClaimOwnership.Valid() || t.Registration == "" || t.Registration != t.ClaimRowID {
17 return nil, ErrClaimOwnership
18 }
19 tx, err := r.sql.Begin()
20 if err != nil {
21 return nil, err
22 }
23 defer tx.Rollback()
24 route, err := scanRoute(tx.QueryRow(`SELECT `+routeCols+` FROM routes WHERE host=?`, t.Host).Scan)
25 if err != nil {
26 if errors.Is(err, sql.ErrNoRows) {
27 return nil, ErrClaimOwnership
28 }
29 return nil, err
30 }
31 if !route.ClaimOwnership.Valid() || route.ClaimIncarnation != t.ClaimIncarnation || route.ClaimStamp != t.ClaimStamp || route.ClaimOwner != t.ClaimOwner {
32 return nil, ErrClaimOwnership
33 }
34 old, err := scanTimer(tx.QueryRow(`SELECT `+timerCols+` FROM timers WHERE host=? AND name=?`, t.Host, t.Name))
35 switch {
36 case errors.Is(err, sql.ErrNoRows):
37 if expected != "" {
38 return nil, ErrTimerChanged
39 }
40 case err != nil:
41 return nil, err
42 default:
43 if old == t {
44 if err := tx.Commit(); err != nil {
45 return nil, err
46 }
47 return &old, nil
48 }
49 if old.Registration == t.Registration {
50 return nil, ErrTimerChanged
51 }
52 if old.Kind != t.Kind {
53 return nil, ErrTimerKind
54 }
55 if expected == "" || old.Registration != expected || old.ClaimRowID != expected || old.ClaimIncarnation != t.ClaimIncarnation || old.ClaimStamp != t.ClaimStamp || old.ClaimOwner != t.ClaimOwner {
56 return nil, ErrTimerChanged
57 }
58 if _, err := tx.Exec(`DELETE FROM timers WHERE host=? AND name=? AND registration=? AND claim_incarnation=? AND claim_stamp=? AND claim_owner=? AND claim_row_id=?`, t.Host, t.Name, expected, t.ClaimIncarnation, t.ClaimStamp, t.ClaimOwner, expected); err != nil {
59 return nil, err
60 }
61 }
62 if _, err := tx.Exec(`INSERT INTO timers (`+timerCols+`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`, t.Host, t.Name, t.Kind, t.Spec, t.Path, t.NextAt, t.LastAt, t.LastStatus, t.LastMs, t.Attempts, t.CreatedAt, t.DueAt, t.Registration, t.ClaimIncarnation, t.ClaimStamp, t.ClaimOwner, t.ClaimRowID); err != nil {
63 return nil, err
64 }
65 if err := tx.Commit(); err != nil {
66 return nil, err
67 }
68 return &t, nil
69}
70
71// DeleteTimerOwnedRegistration cannot remove a successor or a row with a
72// copied registration but another installation's ownership.
73func (r *Registry) DeleteTimerOwnedRegistration(t Timer) error {
74 if r.remote != nil {
75 _, err := r.remote.call(nil, remoteRequest{Method: "DeleteTimerOwnedRegistration", T: t}, nil)
76 return err
77 }
78 if !t.ClaimOwnership.Valid() || t.Registration == "" || t.Registration != t.ClaimRowID {
79 return ErrClaimOwnership
80 }
81 res, err := r.sql.Exec(`DELETE FROM timers WHERE host=? AND name=? AND registration=? AND claim_incarnation=? AND claim_stamp=? AND claim_owner=? AND claim_row_id=?`, t.Host, t.Name, t.Registration, t.ClaimIncarnation, t.ClaimStamp, t.ClaimOwner, t.ClaimRowID)
82 return timerRegistrationResult(res, err)
83}
84