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
import (
"fmt"
"path"
"strconv"
"strings"
"code.gitea.io/gitea/modules/log"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/errors"
"github.com/syndtr/goleveldb/leveldb/opt"
@ -20,8 +22,16 @@ func (m *Manager) CloseLevelDB(connection string) error {
defer m.mutex.Unlock()
db, ok := m.LevelDBConnections[connection]
if !ok {
connection = ToLevelDBURI(connection).String()
db, ok = m.LevelDBConnections[connection]
// Try the full URI
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 {
return nil
@ -40,6 +50,12 @@ func (m *Manager) CloseLevelDB(connection string) error {
// GetLevelDB gets a levelDB for a particular connection
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()
defer m.mutex.Unlock()
db, ok := m.LevelDBConnections[connection]
@ -48,12 +64,28 @@ func (m *Manager) GetLevelDB(connection string) (*leveldb.DB, error) {
return db.db, nil
}
uri := ToLevelDBURI(connection)
db = &levelDBHolder{
name: []string{connection, uri.String()},
db, ok = m.LevelDBConnections[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{}
for k, v := range uri.Query() {
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)
if err != nil {
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)
if err != nil {