code.gitea.io/gitea@v1.19.3/modules/nosql/manager_leveldb.go (about) 1 // Copyright 2020 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package nosql 5 6 import ( 7 "fmt" 8 "path" 9 "runtime/pprof" 10 "strconv" 11 "strings" 12 13 "code.gitea.io/gitea/modules/log" 14 15 "github.com/syndtr/goleveldb/leveldb" 16 "github.com/syndtr/goleveldb/leveldb/errors" 17 "github.com/syndtr/goleveldb/leveldb/opt" 18 ) 19 20 // CloseLevelDB closes a levelDB 21 func (m *Manager) CloseLevelDB(connection string) error { 22 m.mutex.Lock() 23 defer m.mutex.Unlock() 24 db, ok := m.LevelDBConnections[connection] 25 if !ok { 26 // Try the full URI 27 uri := ToLevelDBURI(connection) 28 db, ok = m.LevelDBConnections[uri.String()] 29 30 if !ok { 31 // Try the datadir directly 32 dataDir := path.Join(uri.Host, uri.Path) 33 34 db, ok = m.LevelDBConnections[dataDir] 35 } 36 } 37 if !ok { 38 return nil 39 } 40 41 db.count-- 42 if db.count > 0 { 43 return nil 44 } 45 46 for _, name := range db.name { 47 delete(m.LevelDBConnections, name) 48 } 49 return db.db.Close() 50 } 51 52 // GetLevelDB gets a levelDB for a particular connection 53 func (m *Manager) GetLevelDB(connection string) (db *leveldb.DB, err error) { 54 // Because we want associate any goroutines created by this call to the main nosqldb context we need to 55 // wrap this in a goroutine labelled with the nosqldb context 56 done := make(chan struct{}) 57 var recovered interface{} 58 go func() { 59 defer func() { 60 recovered = recover() 61 if recovered != nil { 62 log.Critical("PANIC during GetLevelDB: %v\nStacktrace: %s", recovered, log.Stack(2)) 63 } 64 close(done) 65 }() 66 pprof.SetGoroutineLabels(m.ctx) 67 68 db, err = m.getLevelDB(connection) 69 }() 70 <-done 71 if recovered != nil { 72 panic(recovered) 73 } 74 return db, err 75 } 76 77 func (m *Manager) getLevelDB(connection string) (*leveldb.DB, error) { 78 // Convert the provided connection description to the common format 79 uri := ToLevelDBURI(connection) 80 81 // Get the datadir 82 dataDir := path.Join(uri.Host, uri.Path) 83 84 m.mutex.Lock() 85 defer m.mutex.Unlock() 86 db, ok := m.LevelDBConnections[connection] 87 if ok { 88 db.count++ 89 90 return db.db, nil 91 } 92 93 db, ok = m.LevelDBConnections[uri.String()] 94 if ok { 95 db.count++ 96 97 return db.db, nil 98 } 99 100 // if there is already a connection to this leveldb reuse that 101 // NOTE: if there differing options then only the first leveldb connection will be used 102 db, ok = m.LevelDBConnections[dataDir] 103 if ok { 104 db.count++ 105 log.Warn("Duplicate connection to level db: %s with different connection strings. Initial connection: %s. This connection: %s", dataDir, db.name[0], connection) 106 db.name = append(db.name, connection) 107 m.LevelDBConnections[connection] = db 108 return db.db, nil 109 } 110 db = &levelDBHolder{ 111 name: []string{connection, uri.String(), dataDir}, 112 } 113 114 opts := &opt.Options{} 115 for k, v := range uri.Query() { 116 switch replacer.Replace(strings.ToLower(k)) { 117 case "blockcachecapacity": 118 opts.BlockCacheCapacity, _ = strconv.Atoi(v[0]) 119 case "blockcacheevictremoved": 120 opts.BlockCacheEvictRemoved, _ = strconv.ParseBool(v[0]) 121 case "blockrestartinterval": 122 opts.BlockRestartInterval, _ = strconv.Atoi(v[0]) 123 case "blocksize": 124 opts.BlockSize, _ = strconv.Atoi(v[0]) 125 case "compactionexpandlimitfactor": 126 opts.CompactionExpandLimitFactor, _ = strconv.Atoi(v[0]) 127 case "compactiongpoverlapsfactor": 128 opts.CompactionGPOverlapsFactor, _ = strconv.Atoi(v[0]) 129 case "compactionl0trigger": 130 opts.CompactionL0Trigger, _ = strconv.Atoi(v[0]) 131 case "compactionsourcelimitfactor": 132 opts.CompactionSourceLimitFactor, _ = strconv.Atoi(v[0]) 133 case "compactiontablesize": 134 opts.CompactionTableSize, _ = strconv.Atoi(v[0]) 135 case "compactiontablesizemultiplier": 136 opts.CompactionTableSizeMultiplier, _ = strconv.ParseFloat(v[0], 64) 137 case "compactiontablesizemultiplierperlevel": 138 for _, val := range v { 139 f, _ := strconv.ParseFloat(val, 64) 140 opts.CompactionTableSizeMultiplierPerLevel = append(opts.CompactionTableSizeMultiplierPerLevel, f) 141 } 142 case "compactiontotalsize": 143 opts.CompactionTotalSize, _ = strconv.Atoi(v[0]) 144 case "compactiontotalsizemultiplier": 145 opts.CompactionTotalSizeMultiplier, _ = strconv.ParseFloat(v[0], 64) 146 case "compactiontotalsizemultiplierperlevel": 147 for _, val := range v { 148 f, _ := strconv.ParseFloat(val, 64) 149 opts.CompactionTotalSizeMultiplierPerLevel = append(opts.CompactionTotalSizeMultiplierPerLevel, f) 150 } 151 case "compression": 152 val, _ := strconv.Atoi(v[0]) 153 opts.Compression = opt.Compression(val) 154 case "disablebufferpool": 155 opts.DisableBufferPool, _ = strconv.ParseBool(v[0]) 156 case "disableblockcache": 157 opts.DisableBlockCache, _ = strconv.ParseBool(v[0]) 158 case "disablecompactionbackoff": 159 opts.DisableCompactionBackoff, _ = strconv.ParseBool(v[0]) 160 case "disablelargebatchtransaction": 161 opts.DisableLargeBatchTransaction, _ = strconv.ParseBool(v[0]) 162 case "errorifexist": 163 opts.ErrorIfExist, _ = strconv.ParseBool(v[0]) 164 case "errorifmissing": 165 opts.ErrorIfMissing, _ = strconv.ParseBool(v[0]) 166 case "iteratorsamplingrate": 167 opts.IteratorSamplingRate, _ = strconv.Atoi(v[0]) 168 case "nosync": 169 opts.NoSync, _ = strconv.ParseBool(v[0]) 170 case "nowritemerge": 171 opts.NoWriteMerge, _ = strconv.ParseBool(v[0]) 172 case "openfilescachecapacity": 173 opts.OpenFilesCacheCapacity, _ = strconv.Atoi(v[0]) 174 case "readonly": 175 opts.ReadOnly, _ = strconv.ParseBool(v[0]) 176 case "strict": 177 val, _ := strconv.Atoi(v[0]) 178 opts.Strict = opt.Strict(val) 179 case "writebuffer": 180 opts.WriteBuffer, _ = strconv.Atoi(v[0]) 181 case "writel0pausetrigger": 182 opts.WriteL0PauseTrigger, _ = strconv.Atoi(v[0]) 183 case "writel0slowdowntrigger": 184 opts.WriteL0SlowdownTrigger, _ = strconv.Atoi(v[0]) 185 case "clientname": 186 db.name = append(db.name, v[0]) 187 } 188 } 189 190 var err error 191 db.db, err = leveldb.OpenFile(dataDir, opts) 192 if err != nil { 193 if !errors.IsCorrupted(err) { 194 if strings.Contains(err.Error(), "resource temporarily unavailable") { 195 err = fmt.Errorf("unable to lock level db at %s: %w", dataDir, err) 196 return nil, err 197 } 198 199 err = fmt.Errorf("unable to open level db at %s: %w", dataDir, err) 200 return nil, err 201 } 202 db.db, err = leveldb.RecoverFile(dataDir, opts) 203 } 204 205 if err != nil { 206 return nil, err 207 } 208 209 for _, name := range db.name { 210 m.LevelDBConnections[name] = db 211 } 212 db.count++ 213 return db.db, nil 214 }