|
4 | 4 | "context"
|
5 | 5 | "database/sql"
|
6 | 6 | "fmt"
|
| 7 | + "go.uber.org/atomic" |
7 | 8 | "io"
|
8 | 9 | "io/ioutil"
|
9 | 10 | nurl "net/url"
|
@@ -46,7 +47,7 @@ type Config struct {
|
46 | 47 |
|
47 | 48 | type CockroachDb struct {
|
48 | 49 | db *sql.DB
|
49 |
| - isLocked bool |
| 50 | + isLocked atomic.Bool |
50 | 51 |
|
51 | 52 | // Open and WithInstance need to guarantee that config is never nil
|
52 | 53 | config *Config
|
@@ -152,71 +153,67 @@ func (c *CockroachDb) Close() error {
|
152 | 153 | // Locking is done manually with a separate lock table. Implementing advisory locks in CRDB is being discussed
|
153 | 154 | // See: https://github.com/cockroachdb/cockroach/issues/13546
|
154 | 155 | func (c *CockroachDb) Lock() error {
|
155 |
| - err := crdb.ExecuteTx(context.Background(), c.db, nil, func(tx *sql.Tx) (err error) { |
156 |
| - aid, err := database.GenerateAdvisoryLockId(c.config.DatabaseName) |
157 |
| - if err != nil { |
158 |
| - return err |
159 |
| - } |
160 |
| - |
161 |
| - query := "SELECT * FROM " + c.config.LockTable + " WHERE lock_id = $1" |
162 |
| - rows, err := tx.Query(query, aid) |
163 |
| - if err != nil { |
164 |
| - return database.Error{OrigErr: err, Err: "failed to fetch migration lock", Query: []byte(query)} |
165 |
| - } |
166 |
| - defer func() { |
167 |
| - if errClose := rows.Close(); errClose != nil { |
168 |
| - err = multierror.Append(err, errClose) |
| 156 | + return database.CasRestoreOnErr(&c.isLocked, false, true, database.ErrLocked, func() (err error) { |
| 157 | + return crdb.ExecuteTx(context.Background(), c.db, nil, func(tx *sql.Tx) (err error) { |
| 158 | + aid, err := database.GenerateAdvisoryLockId(c.config.DatabaseName) |
| 159 | + if err != nil { |
| 160 | + return err |
169 | 161 | }
|
170 |
| - }() |
171 | 162 |
|
172 |
| - // If row exists at all, lock is present |
173 |
| - locked := rows.Next() |
174 |
| - if locked && !c.config.ForceLock { |
175 |
| - return database.ErrLocked |
176 |
| - } |
| 163 | + query := "SELECT * FROM " + c.config.LockTable + " WHERE lock_id = $1" |
| 164 | + rows, err := tx.Query(query, aid) |
| 165 | + if err != nil { |
| 166 | + return database.Error{OrigErr: err, Err: "failed to fetch migration lock", Query: []byte(query)} |
| 167 | + } |
| 168 | + defer func() { |
| 169 | + if errClose := rows.Close(); errClose != nil { |
| 170 | + err = multierror.Append(err, errClose) |
| 171 | + } |
| 172 | + }() |
| 173 | + |
| 174 | + // If row exists at all, lock is present |
| 175 | + locked := rows.Next() |
| 176 | + if locked && !c.config.ForceLock { |
| 177 | + return database.ErrLocked |
| 178 | + } |
177 | 179 |
|
178 |
| - query = "INSERT INTO " + c.config.LockTable + " (lock_id) VALUES ($1)" |
179 |
| - if _, err := tx.Exec(query, aid); err != nil { |
180 |
| - return database.Error{OrigErr: err, Err: "failed to set migration lock", Query: []byte(query)} |
181 |
| - } |
| 180 | + query = "INSERT INTO " + c.config.LockTable + " (lock_id) VALUES ($1)" |
| 181 | + if _, err := tx.Exec(query, aid); err != nil { |
| 182 | + return database.Error{OrigErr: err, Err: "failed to set migration lock", Query: []byte(query)} |
| 183 | + } |
182 | 184 |
|
183 |
| - return nil |
| 185 | + return nil |
| 186 | + }) |
184 | 187 | })
|
185 |
| - |
186 |
| - if err != nil { |
187 |
| - return err |
188 |
| - } else { |
189 |
| - c.isLocked = true |
190 |
| - return nil |
191 |
| - } |
192 | 188 | }
|
193 | 189 |
|
194 | 190 | // Locking is done manually with a separate lock table. Implementing advisory locks in CRDB is being discussed
|
195 | 191 | // See: https://github.com/cockroachdb/cockroach/issues/13546
|
196 | 192 | func (c *CockroachDb) Unlock() error {
|
197 |
| - aid, err := database.GenerateAdvisoryLockId(c.config.DatabaseName) |
198 |
| - if err != nil { |
199 |
| - return err |
200 |
| - } |
| 193 | + return database.CasRestoreOnErr(&c.isLocked, true, false, database.ErrNotLocked, func() (err error) { |
| 194 | + aid, err := database.GenerateAdvisoryLockId(c.config.DatabaseName) |
| 195 | + if err != nil { |
| 196 | + return err |
| 197 | + } |
201 | 198 |
|
202 |
| - // In the event of an implementation (non-migration) error, it is possible for the lock to not be released. Until |
203 |
| - // a better locking mechanism is added, a manual purging of the lock table may be required in such circumstances |
204 |
| - query := "DELETE FROM " + c.config.LockTable + " WHERE lock_id = $1" |
205 |
| - if _, err := c.db.Exec(query, aid); err != nil { |
206 |
| - if e, ok := err.(*pq.Error); ok { |
207 |
| - // 42P01 is "UndefinedTableError" in CockroachDB |
208 |
| - // https://github.com/cockroachdb/cockroach/blob/master/pkg/sql/pgwire/pgerror/codes.go |
209 |
| - if e.Code == "42P01" { |
210 |
| - // On drops, the lock table is fully removed; This is fine, and is a valid "unlocked" state for the schema |
211 |
| - c.isLocked = false |
212 |
| - return nil |
| 199 | + // In the event of an implementation (non-migration) error, it is possible for the lock to not be released. Until |
| 200 | + // a better locking mechanism is added, a manual purging of the lock table may be required in such circumstances |
| 201 | + query := "DELETE FROM " + c.config.LockTable + " WHERE lock_id = $1" |
| 202 | + if _, err := c.db.Exec(query, aid); err != nil { |
| 203 | + if e, ok := err.(*pq.Error); ok { |
| 204 | + // 42P01 is "UndefinedTableError" in CockroachDB |
| 205 | + // https://github.com/cockroachdb/cockroach/blob/master/pkg/sql/pgwire/pgerror/codes.go |
| 206 | + if e.Code == "42P01" { |
| 207 | + // On drops, the lock table is fully removed; This is fine, and is a valid "unlocked" state for the schema |
| 208 | + return nil |
| 209 | + } |
213 | 210 | }
|
| 211 | + |
| 212 | + return database.Error{OrigErr: err, Err: "failed to release migration lock", Query: []byte(query)} |
214 | 213 | }
|
215 |
| - return database.Error{OrigErr: err, Err: "failed to release migration lock", Query: []byte(query)} |
216 |
| - } |
217 | 214 |
|
218 |
| - c.isLocked = false |
219 |
| - return nil |
| 215 | + return nil |
| 216 | + }) |
220 | 217 | }
|
221 | 218 |
|
222 | 219 | func (c *CockroachDb) Run(migration io.Reader) error {
|
|
0 commit comments