@@ -101,52 +101,25 @@ func Open(dirname string, opts *Options) (db *DB, err error) {
101
101
102
102
// In all error cases, we return db = nil; this is used by various
103
103
// deferred cleanups.
104
+ maybeCleanUp := func (fn func () error ) {
105
+ if db == nil {
106
+ err = errors .CombineErrors (err , fn ())
107
+ }
108
+ }
104
109
105
110
// Open the database and WAL directories first.
106
111
walDirname , secondaryWalDirName , dataDir , err := prepareAndOpenDirs (dirname , opts )
107
112
if err != nil {
108
113
return nil , errors .Wrapf (err , "error opening database at %q" , dirname )
109
114
}
110
- defer func () {
111
- if db == nil {
112
- dataDir .Close ()
113
- }
114
- }()
115
-
116
- // Lock all directories that will be written to.
117
- var dirLocks [3 ]* base.DirLock
118
- defer func () {
119
- for _ , lock := range dirLocks {
120
- if db == nil && lock != nil {
121
- _ = lock .Close ()
122
- }
123
- }
124
- }()
115
+ defer maybeCleanUp (dataDir .Close )
125
116
126
117
// Lock the database directory.
127
118
fileLock , err := base .AcquireOrValidateDirectoryLock (opts .Lock , dirname , opts .FS )
128
119
if err != nil {
129
120
return nil , err
130
121
}
131
- dirLocks [0 ] = fileLock
132
-
133
- // Lock the WAL directories, if configured.
134
- var walDirLock , walFailoverLock * base.DirLock
135
- if walDirname != dirname {
136
- walDirLock , err = base .AcquireOrValidateDirectoryLock (opts .WALDirLock , walDirname , opts .FS )
137
- if err != nil {
138
- return nil , err
139
- }
140
- dirLocks [1 ] = walDirLock
141
- }
142
- if opts .WALFailover != nil && secondaryWalDirName != dirname && secondaryWalDirName != walDirname {
143
- walFailoverLock , err = base .AcquireOrValidateDirectoryLock (
144
- opts .WALFailover .Secondary .Lock , secondaryWalDirName , opts .WALFailover .Secondary .FS )
145
- if err != nil {
146
- return nil , err
147
- }
148
- dirLocks [2 ] = walFailoverLock
149
- }
122
+ defer maybeCleanUp (fileLock .Close )
150
123
151
124
// List the directory contents. This also happens to include WAL log files, if
152
125
// they are in the same dir, but we will ignore those below. The provider is
@@ -358,8 +331,9 @@ func Open(dirname string, opts *Options) (db *DB, err error) {
358
331
d .mu .log .metrics .fsyncLatency = prometheus .NewHistogram (prometheus.HistogramOpts {
359
332
Buckets : FsyncLatencyBuckets ,
360
333
})
334
+
361
335
walOpts := wal.Options {
362
- Primary : wal.Dir {Lock : walDirLock , FS : opts .FS , Dirname : walDirname },
336
+ Primary : wal.Dir {FS : opts .FS , Dirname : walDirname },
363
337
Secondary : wal.Dir {},
364
338
MinUnflushedWALNum : wal .NumWAL (d .mu .versions .minUnflushedLogNum ),
365
339
MaxNumRecyclableLogs : opts .MemTableStopWritesThreshold + 1 ,
@@ -373,10 +347,45 @@ func Open(dirname string, opts *Options) (db *DB, err error) {
373
347
EventListener : walEventListenerAdaptor {l : opts .EventListener },
374
348
WriteWALSyncOffsets : func () bool { return d .FormatMajorVersion () >= FormatWALSyncChunks },
375
349
}
350
+ // Ensure we release the WAL directory locks if we fail to open the
351
+ // database. If we fail before initializing the WAL manager, this defer is
352
+ // responsible for releasing the locks. If we fail after initializing the
353
+ // WAL manager, closing the WAL manager will release the locks.
354
+ //
355
+ // TODO(jackson): Open's cleanup error handling logic is convoluted; can we
356
+ // simplify it?
357
+ defer maybeCleanUp (func () (err error ) {
358
+ if d .mu .log .manager == nil {
359
+ if walOpts .Primary .Lock != nil {
360
+ err = errors .CombineErrors (err , walOpts .Primary .Lock .Close ())
361
+ }
362
+ if walOpts .Secondary .Lock != nil {
363
+ err = errors .CombineErrors (err , walOpts .Secondary .Lock .Close ())
364
+ }
365
+ return err
366
+ }
367
+ return nil
368
+ })
369
+
370
+ // Lock the dedicated WAL directory, if configured.
371
+ if walDirname != dirname {
372
+ walOpts .Primary .Lock , err = base .AcquireOrValidateDirectoryLock (opts .WALDirLock , walDirname , opts .FS )
373
+ if err != nil {
374
+ return nil , err
375
+ }
376
+ }
376
377
if opts .WALFailover != nil {
377
378
walOpts .Secondary = opts .WALFailover .Secondary
379
+ // Lock the secondary WAL directory, if distinct from the data directory
380
+ // and primary WAL directory.
381
+ if secondaryWalDirName != dirname && secondaryWalDirName != walDirname {
382
+ walOpts .Secondary .Lock , err = base .AcquireOrValidateDirectoryLock (
383
+ opts .WALFailover .Secondary .Lock , secondaryWalDirName , opts .WALFailover .Secondary .FS )
384
+ if err != nil {
385
+ return nil , err
386
+ }
387
+ }
378
388
walOpts .Secondary .Dirname = secondaryWalDirName
379
- walOpts .Secondary .Lock = walFailoverLock
380
389
walOpts .FailoverOptions = opts .WALFailover .FailoverOptions
381
390
walOpts .FailoverWriteAndSyncLatency = prometheus .NewHistogram (prometheus.HistogramOpts {
382
391
Buckets : FsyncLatencyBuckets ,
@@ -416,12 +425,7 @@ func Open(dirname string, opts *Options) (db *DB, err error) {
416
425
if err != nil {
417
426
return nil , err
418
427
}
419
- defer func () {
420
- if db == nil {
421
- _ = walManager .Close ()
422
- }
423
- }()
424
-
428
+ defer maybeCleanUp (walManager .Close )
425
429
d .mu .log .manager = walManager
426
430
427
431
d .cleanupManager = openCleanupManager (opts , d .objProvider , d .getDeletionPacerInfo )
0 commit comments