| 1 | package registry |
| 2 | |
| 3 | import ( |
| 4 | "database/sql" |
| 5 | "errors" |
| 6 | "time" |
| 7 | ) |
| 8 | |
| 9 | // The box's fence generation source. The activator's fences live in the |
| 10 | // agent's memory and die with it, but a generation outlives the process: |
| 11 | // the import journal stores the one its rollback fences under |
| 12 | // (CloseImportGate), and a resumed rollback recognises and lifts its fence |
| 13 | // by that number. A generation handed out after a restart must therefore |
| 14 | // never equal one handed out before it, or the resumed rollback could take |
| 15 | // another operation's fence for its own, or lift it. |
| 16 | // |
| 17 | // Each generation is the later of the wall clock in nanoseconds (the space |
| 18 | // the layout pass and the moves cut draw theirs from) and one past the |
| 19 | // last generation this registry handed out, which is stored before it is |
| 20 | // returned. The stored floor survives a restart, so a clock that went back |
| 21 | // cannot repeat a number; the clock covers a registry restored from an |
| 22 | // older snapshot. |
| 23 | |
| 24 | func (r *Registry) migrateFenceGenerations() error { |
| 25 | _, err := r.sql.Exec(`CREATE TABLE IF NOT EXISTS fence_generation ( |
| 26 | id INTEGER PRIMARY KEY CHECK (id = 1), |
| 27 | last INTEGER NOT NULL |
| 28 | );`) |
| 29 | return err |
| 30 | } |
| 31 | |
| 32 | // NextFenceGeneration hands out a fence generation no earlier call on this |
| 33 | // registry file returned, across restarts: never 0, and above every |
| 34 | // generation it handed out before. now is the wall clock. |
| 35 | func (r *Registry) NextFenceGeneration(now time.Time) (uint64, error) { |
| 36 | var next int64 |
| 37 | err := r.inTx(func(tx txer) error { |
| 38 | var last int64 |
| 39 | err := tx.QueryRow(`SELECT last FROM fence_generation WHERE id = 1`).Scan(&last) |
| 40 | if err != nil && !errors.Is(err, sql.ErrNoRows) { |
| 41 | return err |
| 42 | } |
| 43 | next = max(now.UnixNano(), last+1, 1) |
| 44 | _, err = tx.Exec(`INSERT INTO fence_generation (id, last) VALUES (1, ?) |
| 45 | ON CONFLICT(id) DO UPDATE SET last = excluded.last`, next) |
| 46 | return err |
| 47 | }) |
| 48 | if err != nil { |
| 49 | return 0, err |
| 50 | } |
| 51 | return uint64(next), nil |
| 52 | } |
| 53 | |