package registry import ( "database/sql" "errors" "time" ) // The box's fence generation source. The activator's fences live in the // agent's memory and die with it, but a generation outlives the process: // the import journal stores the one its rollback fences under // (CloseImportGate), and a resumed rollback recognises and lifts its fence // by that number. A generation handed out after a restart must therefore // never equal one handed out before it, or the resumed rollback could take // another operation's fence for its own, or lift it. // // Each generation is the later of the wall clock in nanoseconds (the space // the layout pass and the moves cut draw theirs from) and one past the // last generation this registry handed out, which is stored before it is // returned. The stored floor survives a restart, so a clock that went back // cannot repeat a number; the clock covers a registry restored from an // older snapshot. func (r *Registry) migrateFenceGenerations() error { _, err := r.sql.Exec(`CREATE TABLE IF NOT EXISTS fence_generation ( id INTEGER PRIMARY KEY CHECK (id = 1), last INTEGER NOT NULL );`) return err } // NextFenceGeneration hands out a fence generation no earlier call on this // registry file returned, across restarts: never 0, and above every // generation it handed out before. now is the wall clock. func (r *Registry) NextFenceGeneration(now time.Time) (uint64, error) { var next int64 err := r.inTx(func(tx txer) error { var last int64 err := tx.QueryRow(`SELECT last FROM fence_generation WHERE id = 1`).Scan(&last) if err != nil && !errors.Is(err, sql.ErrNoRows) { return err } next = max(now.UnixNano(), last+1, 1) _, err = tx.Exec(`INSERT INTO fence_generation (id, last) VALUES (1, ?) ON CONFLICT(id) DO UPDATE SET last = excluded.last`, next) return err }) if err != nil { return 0, err } return uint64(next), nil }