Adjust error for already locked db and prevent level db lock on malformed connstr (#18923) (#18938)

Backport #18923

This PR adjusts the error returned when there is failure to lock the level db, and
permits a connections to the same leveldb where there is a different connection string.

Reference #18921
Reference #18917

Signed-off-by: Andrew Thornton <art27@cantab.net>
This commit is contained in:
zeripath 2022-02-28 15:45:38 +00:00 committed by GitHub
parent 4384b85046
commit 5f9c18b2b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 43 additions and 7 deletions

View File

@ -5,10 +5,12 @@
package nosql package nosql
import ( import (
"fmt"
"path" "path"
"strconv" "strconv"
"strings" "strings"
"code.gitea.io/gitea/modules/log"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/errors" "github.com/syndtr/goleveldb/leveldb/errors"
"github.com/syndtr/goleveldb/leveldb/opt" "github.com/syndtr/goleveldb/leveldb/opt"
@ -20,8 +22,16 @@ func (m *Manager) CloseLevelDB(connection string) error {
defer m.mutex.Unlock() defer m.mutex.Unlock()
db, ok := m.LevelDBConnections[connection] db, ok := m.LevelDBConnections[connection]
if !ok { if !ok {
connection = ToLevelDBURI(connection).String() // Try the full URI
db, ok = m.LevelDBConnections[connection] uri := ToLevelDBURI(connection)
db, ok = m.LevelDBConnections[uri.String()]
if !ok {
// Try the datadir directly
dataDir := path.Join(uri.Host, uri.Path)
db, ok = m.LevelDBConnections[dataDir]
}
} }
if !ok { if !ok {
return nil return nil
@ -40,6 +50,12 @@ func (m *Manager) CloseLevelDB(connection string) error {
// GetLevelDB gets a levelDB for a particular connection // GetLevelDB gets a levelDB for a particular connection
func (m *Manager) GetLevelDB(connection string) (*leveldb.DB, error) { func (m *Manager) GetLevelDB(connection string) (*leveldb.DB, error) {
// Convert the provided connection description to the common format
uri := ToLevelDBURI(connection)
// Get the datadir
dataDir := path.Join(uri.Host, uri.Path)
m.mutex.Lock() m.mutex.Lock()
defer m.mutex.Unlock() defer m.mutex.Unlock()
db, ok := m.LevelDBConnections[connection] db, ok := m.LevelDBConnections[connection]
@ -48,12 +64,28 @@ func (m *Manager) GetLevelDB(connection string) (*leveldb.DB, error) {
return db.db, nil return db.db, nil
} }
uri := ToLevelDBURI(connection)
db = &levelDBHolder{ db, ok = m.LevelDBConnections[uri.String()]
name: []string{connection, uri.String()}, if ok {
db.count++
return db.db, nil
}
// if there is already a connection to this leveldb reuse that
// NOTE: if there differing options then only the first leveldb connection will be used
db, ok = m.LevelDBConnections[dataDir]
if ok {
db.count++
log.Warn("Duplicate connnection to level db: %s with different connection strings. Initial connection: %s. This connection: %s", dataDir, db.name[0], connection)
db.name = append(db.name, connection)
m.LevelDBConnections[connection] = db
return db.db, nil
}
db = &levelDBHolder{
name: []string{connection, uri.String(), dataDir},
} }
dataDir := path.Join(uri.Host, uri.Path)
opts := &opt.Options{} opts := &opt.Options{}
for k, v := range uri.Query() { for k, v := range uri.Query() {
switch replacer.Replace(strings.ToLower(k)) { switch replacer.Replace(strings.ToLower(k)) {
@ -134,7 +166,11 @@ func (m *Manager) GetLevelDB(connection string) (*leveldb.DB, error) {
db.db, err = leveldb.OpenFile(dataDir, opts) db.db, err = leveldb.OpenFile(dataDir, opts)
if err != nil { if err != nil {
if !errors.IsCorrupted(err) { if !errors.IsCorrupted(err) {
return nil, err if strings.Contains(err.Error(), "resource temporarily unavailable") {
return nil, fmt.Errorf("unable to lock level db at %s: %w", dataDir, err)
}
return nil, fmt.Errorf("unable to open level db at %s: %w", dataDir, err)
} }
db.db, err = leveldb.RecoverFile(dataDir, opts) db.db, err = leveldb.RecoverFile(dataDir, opts)
if err != nil { if err != nil {