kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/storage/leveldb/leveldb.go (about) 1 /* 2 * Copyright 2014 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // Package leveldb implements a graphstore.Service using a LevelDB backend 18 // database. 19 package leveldb // import "kythe.io/kythe/go/storage/leveldb" 20 21 import ( 22 "bytes" 23 "context" 24 "fmt" 25 "io" 26 "os" 27 28 "kythe.io/kythe/go/services/graphstore" 29 "kythe.io/kythe/go/storage/gsutil" 30 "kythe.io/kythe/go/storage/keyvalue" 31 32 "github.com/jmhodges/levigo" 33 ) 34 35 func init() { 36 gsutil.Register("leveldb", func(spec string) (graphstore.Service, error) { return OpenGraphStore(spec, nil) }) 37 gsutil.RegisterDefault("leveldb") 38 } 39 40 // levelDB is a wrapper around a levigo.DB that implements keyvalue.DB 41 type levelDB struct { 42 db *levigo.DB 43 cache *levigo.Cache 44 45 // save options to reduce number of allocations during high load 46 readOpts *levigo.ReadOptions 47 largeReadOpts *levigo.ReadOptions 48 writeOpts *levigo.WriteOptions 49 } 50 51 // CompactRange runs a manual compaction on the Range of keys given. 52 // If r == nil, the entire table will be compacted. 53 func CompactRange(path string, r *keyvalue.Range) error { 54 options := levigo.NewOptions() 55 defer options.Close() 56 db, err := levigo.Open(path, options) 57 if err != nil { 58 return err 59 } 60 lr := levigo.Range{} 61 if r != nil { 62 lr.Start = r.Start 63 lr.Limit = r.End 64 } 65 db.CompactRange(lr) 66 db.Close() 67 return nil 68 } 69 70 // DefaultOptions is the default Options struct passed to Open when not 71 // otherwise given one. 72 var DefaultOptions = &Options{ 73 CacheCapacity: 512 * 1024 * 1024, // 512mb 74 WriteBufferSize: 60 * 1024 * 1024, // 60mb 75 } 76 77 // Options for customizing a LevelDB backend. 78 type Options struct { 79 // CacheCapacity is the caching capacity (in bytes) used for the LevelDB. 80 CacheCapacity int 81 82 // CacheLargeReads determines whether to use the cache for large reads. This 83 // is usually discouraged but may be useful when the entire LevelDB is known 84 // to fit into the cache. 85 CacheLargeReads bool 86 87 // WriteBufferSize is the number of bytes the database will build up in memory 88 // (backed by a disk log) before writing to the on-disk table. 89 WriteBufferSize int 90 91 // MustExist ensures that the given database exists before opening it. If 92 // false and the database does not exist, it will be created. 93 MustExist bool 94 } 95 96 // ValidDB determines if the given path could be a LevelDB database. 97 func ValidDB(path string) bool { 98 stat, err := os.Stat(path) 99 return os.IsNotExist(err) || (err == nil && stat.IsDir()) 100 } 101 102 // OpenGraphStore returns a graphstore.Service backed by a LevelDB database at 103 // the given filepath. If opts==nil, the DefaultOptions are used. 104 func OpenGraphStore(path string, opts *Options) (graphstore.Service, error) { 105 db, err := Open(path, opts) 106 if err != nil { 107 return nil, err 108 } 109 return keyvalue.NewGraphStore(db), nil 110 } 111 112 // Open returns a keyvalue DB backed by a LevelDB database at the given 113 // filepath. If opts==nil, the DefaultOptions are used. 114 func Open(path string, opts *Options) (keyvalue.DB, error) { 115 if opts == nil { 116 opts = DefaultOptions 117 } 118 119 options := levigo.NewOptions() 120 defer options.Close() 121 cache := levigo.NewLRUCache(opts.CacheCapacity) 122 options.SetCache(cache) 123 options.SetCreateIfMissing(!opts.MustExist) 124 if opts.WriteBufferSize > 0 { 125 options.SetWriteBufferSize(opts.WriteBufferSize) 126 } 127 db, err := levigo.Open(path, options) 128 if err != nil { 129 return nil, fmt.Errorf("could not open LevelDB at %q: %v", path, err) 130 } 131 largeReadOpts := levigo.NewReadOptions() 132 largeReadOpts.SetFillCache(opts.CacheLargeReads) 133 return &levelDB{ 134 db: db, 135 cache: cache, 136 readOpts: levigo.NewReadOptions(), 137 largeReadOpts: largeReadOpts, 138 writeOpts: levigo.NewWriteOptions(), 139 }, nil 140 } 141 142 // Close will close the underlying LevelDB database. 143 func (s *levelDB) Close(_ context.Context) error { 144 s.db.Close() 145 s.cache.Close() 146 s.readOpts.Close() 147 s.largeReadOpts.Close() 148 s.writeOpts.Close() 149 return nil 150 } 151 152 type snapshot struct { 153 db *levigo.DB 154 s *levigo.Snapshot 155 } 156 157 // NewSnapshot implements part of the keyvalue.DB interface. 158 func (s *levelDB) NewSnapshot(_ context.Context) keyvalue.Snapshot { 159 return &snapshot{s.db, s.db.NewSnapshot()} 160 } 161 162 // Close implements part of the keyvalue.Snapshot interface. 163 func (s *snapshot) Close() error { 164 s.db.ReleaseSnapshot(s.s) 165 return nil 166 } 167 168 // Writer implements part of the keyvalue.DB interface. 169 func (s *levelDB) Writer(_ context.Context) (keyvalue.Writer, error) { 170 return &writer{s, levigo.NewWriteBatch()}, nil 171 } 172 173 // Get implements part of the keyvalue.DB interface. 174 func (s *levelDB) Get(_ context.Context, key []byte, opts *keyvalue.Options) ([]byte, error) { 175 ro := s.readOptions(opts) 176 if ro != s.largeReadOpts && ro != s.readOpts { 177 defer ro.Close() 178 } 179 v, err := s.db.Get(ro, key) 180 if err != nil { 181 return nil, err 182 } else if v == nil { 183 return nil, io.EOF 184 } 185 return v, nil 186 } 187 188 // ScanPrefix implements part of the keyvalue.DB interface. 189 func (s *levelDB) ScanPrefix(_ context.Context, prefix []byte, opts *keyvalue.Options) (keyvalue.Iterator, error) { 190 iter, ro := s.iterator(opts) 191 if len(prefix) == 0 { 192 iter.SeekToFirst() 193 } else { 194 iter.Seek(prefix) 195 } 196 return &iterator{iter, ro, prefix, nil}, nil 197 } 198 199 // ScanRange implements part of the keyvalue.DB interface. 200 func (s *levelDB) ScanRange(_ context.Context, r *keyvalue.Range, opts *keyvalue.Options) (keyvalue.Iterator, error) { 201 iter, ro := s.iterator(opts) 202 iter.Seek(r.Start) 203 return &iterator{iter, ro, nil, r}, nil 204 } 205 206 func (s *levelDB) readOptions(opts *keyvalue.Options) *levigo.ReadOptions { 207 if snap := opts.GetSnapshot(); snap != nil { 208 ro := levigo.NewReadOptions() 209 ro.SetSnapshot(snap.(*snapshot).s) 210 ro.SetFillCache(!opts.IsLargeRead()) 211 return ro 212 } 213 if opts.IsLargeRead() { 214 return s.largeReadOpts 215 } 216 return s.readOpts 217 } 218 219 // iterator creates a new levigo Iterator based on the given options. It also 220 // returns any ReadOptions that should be Closed once the Iterator is Closed. 221 func (s *levelDB) iterator(opts *keyvalue.Options) (*levigo.Iterator, *levigo.ReadOptions) { 222 ro := s.readOptions(opts) 223 it := s.db.NewIterator(ro) 224 if ro == s.largeReadOpts || ro == s.readOpts { 225 ro = nil 226 } 227 return it, ro 228 } 229 230 type writer struct { 231 s *levelDB 232 *levigo.WriteBatch 233 } 234 235 // Write implements part of the keyvalue.Writer interface. 236 func (w *writer) Write(key, val []byte) error { 237 w.Put(key, val) 238 return nil 239 } 240 241 // Close implements part of the keyvalue.Writer interface. 242 func (w *writer) Close() error { 243 if err := w.s.db.Write(w.s.writeOpts, w.WriteBatch); err != nil { 244 return err 245 } 246 w.WriteBatch.Close() 247 return nil 248 } 249 250 type iterator struct { 251 it *levigo.Iterator 252 opts *levigo.ReadOptions 253 254 prefix []byte 255 r *keyvalue.Range 256 } 257 258 // Close implements part of the keyvalue.Iterator interface. 259 func (i iterator) Close() error { 260 if i.opts != nil { 261 i.opts.Close() 262 } 263 i.it.Close() 264 return nil 265 } 266 267 // Next implements part of the keyvalue.Iterator interface. 268 func (i iterator) Next() ([]byte, []byte, error) { 269 if !i.it.Valid() { 270 if err := i.it.GetError(); err != nil { 271 return nil, nil, err 272 } 273 return nil, nil, io.EOF 274 } 275 key, val := i.it.Key(), i.it.Value() 276 if (i.r == nil && !bytes.HasPrefix(key, i.prefix)) || (i.r != nil && bytes.Compare(key, i.r.End) >= 0) { 277 return nil, nil, io.EOF 278 } 279 i.it.Next() 280 return key, val, nil 281 } 282 283 // Seek implements part of the keyvalue.Iterator interface. 284 func (i *iterator) Seek(k []byte) error { 285 i.it.Seek(k) 286 if !i.it.Valid() { 287 if err := i.it.GetError(); err != nil { 288 return err 289 } 290 return io.EOF 291 } 292 return nil 293 }