@@ -2,7 +2,9 @@ package dbconn
2
2
3
3
import (
4
4
"context"
5
+ "crypto/sha1"
5
6
"database/sql"
7
+ "encoding/hex"
6
8
"errors"
7
9
"fmt"
8
10
"time"
@@ -25,6 +27,7 @@ type MetadataLock struct {
25
27
closeCh chan error
26
28
refreshInterval time.Duration
27
29
db * sql.DB
30
+ lockName string
28
31
}
29
32
30
33
func NewMetadataLock (ctx context.Context , dsn string , table * table.TableInfo , logger loggers.Advanced , optionFns ... func (* MetadataLock )) (* MetadataLock , error ) {
@@ -60,32 +63,28 @@ func NewMetadataLock(ctx context.Context, dsn string, table *table.TableInfo, lo
60
63
// The hash is truncated to 8 characters to avoid the maximum lock length.
61
64
// bizarrely_long_schema_name.thisisareallylongtablenamethisisareallylongtablename60charac ==>
62
65
// bizarrely_long_schem.thisisareallylongtablenamethisis-66fec116
63
- //
64
- // The computation of the hash is done server-side to simplify the whole process,
65
- // but that means we can't easily log the actual lock name used. If you want to do that
66
- // in the future, just add another MySQL round-trip to compute the lock name server-side
67
- // and then use the returned string in the GET_LOCK call.
68
- stmt := sqlescape .MustEscapeSQL ("SELECT GET_LOCK( concat(left(%?,20),'.',left(%?,32),'-',left(sha1(concat(%?,%?)),8)), %?)" , table .SchemaName , table .TableName , table .SchemaName , table .TableName , getLockTimeout .Seconds ())
66
+ mdl .lockName = computeLockName (table )
67
+ stmt := sqlescape .MustEscapeSQL ("SELECT GET_LOCK(%?, %?)" , mdl .lockName , getLockTimeout .Seconds ())
69
68
if err := mdl .db .QueryRowContext (ctx , stmt ).Scan (& answer ); err != nil {
70
69
return fmt .Errorf ("could not acquire metadata lock: %s" , err )
71
70
}
72
71
if answer == 0 {
73
72
// 0 means the lock is held by another connection
74
73
// TODO: we could lookup the connection that holds the lock and report details about it
75
- logger .Warnf ("could not acquire metadata lock for %s.%s , lock is held by another connection" , table . SchemaName , table . TableName )
74
+ logger .Warnf ("could not acquire metadata lock for %s, lock is held by another connection" , mdl . lockName )
76
75
77
76
// TODO: we could deal in error codes instead of string contains checks.
78
- return fmt .Errorf ("could not acquire metadata lock for %s.%s , lock is held by another connection" , table . SchemaName , table . TableName )
77
+ return fmt .Errorf ("could not acquire metadata lock for %s, lock is held by another connection" , mdl . lockName )
79
78
} else if answer != 1 {
80
79
// probably we never get here, but just in case
81
- return fmt .Errorf ("could not acquire metadata lock for %s.%s , GET_LOCK returned: %d" , table . SchemaName , table . TableName , answer )
80
+ return fmt .Errorf ("could not acquire metadata lock %s , GET_LOCK returned: %d" , mdl . lockName , answer )
82
81
}
83
82
return nil
84
83
}
85
84
86
85
// Acquire the lock or return an error immediately
87
86
// We only Infof the initial acquisition.
88
- logger .Infof ("attempting to acquire metadata lock for %s.%s " , table . SchemaName , table . TableName )
87
+ logger .Infof ("attempting to acquire metadata lock %s " , mdl . lockName )
89
88
if err = getLock (); err != nil {
90
89
return nil , err
91
90
}
@@ -101,7 +100,7 @@ func NewMetadataLock(ctx context.Context, dsn string, table *table.TableInfo, lo
101
100
select {
102
101
case <- ctx .Done ():
103
102
// Close the dedicated connection to release the lock
104
- logger .Warnf ("releasing metadata lock for %s.%s " , table . SchemaName , table . TableName )
103
+ logger .Warnf ("releasing metadata lock for %s" , mdl . lockName )
105
104
mdl .closeCh <- mdl .db .Close ()
106
105
return
107
106
case <- ticker .C :
@@ -134,7 +133,7 @@ func NewMetadataLock(ctx context.Context, dsn string, table *table.TableInfo, lo
134
133
135
134
logger .Infof ("re-acquired metadata lock after re-establishing connection: %s.%s" , table .SchemaName , table .TableName )
136
135
} else {
137
- logger .Debugf ("refreshed metadata lock for %s.%s " , table . SchemaName , table . TableName )
136
+ logger .Debugf ("refreshed metadata lock for %s" , mdl . lockName )
138
137
}
139
138
}
140
139
}
@@ -159,3 +158,26 @@ func (m *MetadataLock) CloseDBConnection(logger loggers.Advanced) error {
159
158
}
160
159
return nil
161
160
}
161
+
162
+ func (m * MetadataLock ) GetLockName () string {
163
+ return m .lockName
164
+ }
165
+
166
+ func computeLockName (table * table.TableInfo ) string {
167
+ schemaNamePart := table .SchemaName
168
+ if len (schemaNamePart ) > 20 {
169
+ schemaNamePart = schemaNamePart [:20 ]
170
+ }
171
+
172
+ tableNamePart := table .TableName
173
+ if len (tableNamePart ) > 32 {
174
+ tableNamePart = tableNamePart [:32 ]
175
+ }
176
+
177
+ hash := sha1 .New ()
178
+ hash .Write ([]byte (table .SchemaName + table .TableName ))
179
+ hashPart := hex .EncodeToString (hash .Sum (nil ))[:8 ]
180
+
181
+ lockName := fmt .Sprintf ("%s.%s-%s" , schemaNamePart , tableNamePart , hashPart )
182
+ return lockName
183
+ }
0 commit comments