Monorepo for Tangled
tangled.org
1package db
2
3import (
4 "database/sql"
5)
6
7// GetOrAssignOwnerUID returns the virtual UID for ownerDID, minting a new one
8// from the uid_counter table if this owner has not been seen before.
9// UIDs start at 100000 and increment by one per unique owner.
10//
11// A process-wide mutex serialises concurrent callers so two simultaneous
12// requests for distinct DIDs do not race to claim the same counter value.
13// The mutex is local to this DB instance and SQLite itself only permits one
14// writer at a time, so this does not impede throughput in practice.
15func (d *DB) GetOrAssignOwnerUID(ownerDID string) (uint32, error) {
16 d.uidAssignMu.Lock()
17 defer d.uidAssignMu.Unlock()
18
19 tx, err := d.db.Begin()
20 if err != nil {
21 return 0, err
22 }
23 defer tx.Rollback()
24
25 var uid uint32
26 err = tx.QueryRow(
27 `SELECT uid FROM owner_uid_assignments WHERE owner_did = ?`,
28 ownerDID,
29 ).Scan(&uid)
30 if err == nil {
31 return uid, tx.Commit()
32 }
33 if err != sql.ErrNoRows {
34 return 0, err
35 }
36
37 if err := tx.QueryRow(`SELECT next_uid FROM uid_counter`).Scan(&uid); err != nil {
38 return 0, err
39 }
40 if _, err := tx.Exec(`UPDATE uid_counter SET next_uid = next_uid + 1`); err != nil {
41 return 0, err
42 }
43 if _, err := tx.Exec(
44 `INSERT INTO owner_uid_assignments (owner_did, uid) VALUES (?, ?)`,
45 ownerDID, uid,
46 ); err != nil {
47 return 0, err
48 }
49
50 return uid, tx.Commit()
51}
52
53// AllReposForMigration returns all (repo_did, owner_did) pairs with a
54// non-null owner. Pass force=true to include already-migrated repos.
55func (d *DB) AllReposForMigration(force bool) ([]RepoMigrationRow, error) {
56 query := `SELECT repo_did, owner_did FROM repo_keys WHERE owner_did IS NOT NULL`
57 if !force {
58 query += ` AND isolated_at IS NULL`
59 }
60 rows, err := d.db.Query(query)
61 if err != nil {
62 return nil, err
63 }
64 defer rows.Close()
65
66 var result []RepoMigrationRow
67 for rows.Next() {
68 var r RepoMigrationRow
69 if err := rows.Scan(&r.RepoDID, &r.OwnerDID); err != nil {
70 return nil, err
71 }
72 result = append(result, r)
73 }
74 return result, rows.Err()
75}
76
77// CountUnmigratedRepos returns the number of repos that have not yet been
78// isolation-migrated.
79func (d *DB) CountUnmigratedRepos() (int, error) {
80 var n int
81 err := d.db.QueryRow(`
82 SELECT count(1) FROM repo_keys
83 WHERE owner_did IS NOT NULL
84 AND isolated_at IS NULL
85 `).Scan(&n)
86 return n, err
87}
88
89// MarkRepoIsolated sets isolated_at to the current time for repoDID.
90func (d *DB) MarkRepoIsolated(repoDID string) error {
91 _, err := d.db.Exec(
92 `UPDATE repo_keys SET isolated_at = strftime('%Y-%m-%dT%H:%M:%SZ','now') WHERE repo_did = ?`,
93 repoDID,
94 )
95 return err
96}
97
98// RepoMigrationRow is a row returned by AllReposForMigration.
99type RepoMigrationRow struct {
100 RepoDID string
101 OwnerDID string
102}