github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/swarm/storage/localstore/localstore.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package localstore 18 19 import ( 20 "encoding/binary" 21 "errors" 22 "sync" 23 "time" 24 25 "github.com/ethereum/go-ethereum/log" 26 "github.com/ethereum/go-ethereum/swarm/chunk" 27 "github.com/ethereum/go-ethereum/swarm/shed" 28 "github.com/ethereum/go-ethereum/swarm/storage/mock" 29 ) 30 31 var ( 32 // ErrInvalidMode is retuned when an unknown Mode 33 // is provided to the function. 34 ErrInvalidMode = errors.New("invalid mode") 35 // ErrAddressLockTimeout is returned when the same chunk 36 // is updated in parallel and one of the updates 37 // takes longer then the configured timeout duration. 38 ErrAddressLockTimeout = errors.New("address lock timeout") 39 ) 40 41 var ( 42 // Default value for Capacity DB option. 43 defaultCapacity uint64 = 5000000 44 // Limit the number of goroutines created by Getters 45 // that call updateGC function. Value 0 sets no limit. 46 maxParallelUpdateGC = 1000 47 ) 48 49 // DB is the local store implementation and holds 50 // database related objects. 51 type DB struct { 52 shed *shed.DB 53 54 // schema name of loaded data 55 schemaName shed.StringField 56 57 // retrieval indexes 58 retrievalDataIndex shed.Index 59 retrievalAccessIndex shed.Index 60 // push syncing index 61 pushIndex shed.Index 62 // push syncing subscriptions triggers 63 pushTriggers []chan struct{} 64 pushTriggersMu sync.RWMutex 65 66 // pull syncing index 67 pullIndex shed.Index 68 // pull syncing subscriptions triggers per bin 69 pullTriggers map[uint8][]chan struct{} 70 pullTriggersMu sync.RWMutex 71 72 // garbage collection index 73 gcIndex shed.Index 74 75 // field that stores number of intems in gc index 76 gcSize shed.Uint64Field 77 78 // garbage collection is triggered when gcSize exceeds 79 // the capacity value 80 capacity uint64 81 82 // triggers garbage collection event loop 83 collectGarbageTrigger chan struct{} 84 85 // a buffered channel acting as a semaphore 86 // to limit the maximal number of goroutines 87 // created by Getters to call updateGC function 88 updateGCSem chan struct{} 89 // a wait group to ensure all updateGC goroutines 90 // are done before closing the database 91 updateGCWG sync.WaitGroup 92 93 baseKey []byte 94 95 batchMu sync.Mutex 96 97 // this channel is closed when close function is called 98 // to terminate other goroutines 99 close chan struct{} 100 101 // protect Close method from exiting before 102 // garbage collection and gc size write workers 103 // are done 104 collectGarbageWorkerDone chan struct{} 105 } 106 107 // Options struct holds optional parameters for configuring DB. 108 type Options struct { 109 // MockStore is a mock node store that is used to store 110 // chunk data in a central store. It can be used to reduce 111 // total storage space requirements in testing large number 112 // of swarm nodes with chunk data deduplication provided by 113 // the mock global store. 114 MockStore *mock.NodeStore 115 // Capacity is a limit that triggers garbage collection when 116 // number of items in gcIndex equals or exceeds it. 117 Capacity uint64 118 // MetricsPrefix defines a prefix for metrics names. 119 MetricsPrefix string 120 } 121 122 // New returns a new DB. All fields and indexes are initialized 123 // and possible conflicts with schema from existing database is checked. 124 // One goroutine for writing batches is created. 125 func New(path string, baseKey []byte, o *Options) (db *DB, err error) { 126 if o == nil { 127 o = new(Options) 128 } 129 db = &DB{ 130 capacity: o.Capacity, 131 baseKey: baseKey, 132 // channel collectGarbageTrigger 133 // needs to be buffered with the size of 1 134 // to signal another event if it 135 // is triggered during already running function 136 collectGarbageTrigger: make(chan struct{}, 1), 137 close: make(chan struct{}), 138 collectGarbageWorkerDone: make(chan struct{}), 139 } 140 if db.capacity <= 0 { 141 db.capacity = defaultCapacity 142 } 143 if maxParallelUpdateGC > 0 { 144 db.updateGCSem = make(chan struct{}, maxParallelUpdateGC) 145 } 146 147 db.shed, err = shed.NewDB(path, o.MetricsPrefix) 148 if err != nil { 149 return nil, err 150 } 151 // Identify current storage schema by arbitrary name. 152 db.schemaName, err = db.shed.NewStringField("schema-name") 153 if err != nil { 154 return nil, err 155 } 156 // Persist gc size. 157 db.gcSize, err = db.shed.NewUint64Field("gc-size") 158 if err != nil { 159 return nil, err 160 } 161 // Functions for retrieval data index. 162 var ( 163 encodeValueFunc func(fields shed.Item) (value []byte, err error) 164 decodeValueFunc func(keyItem shed.Item, value []byte) (e shed.Item, err error) 165 ) 166 if o.MockStore != nil { 167 encodeValueFunc = func(fields shed.Item) (value []byte, err error) { 168 b := make([]byte, 8) 169 binary.BigEndian.PutUint64(b, uint64(fields.StoreTimestamp)) 170 err = o.MockStore.Put(fields.Address, fields.Data) 171 if err != nil { 172 return nil, err 173 } 174 return b, nil 175 } 176 decodeValueFunc = func(keyItem shed.Item, value []byte) (e shed.Item, err error) { 177 e.StoreTimestamp = int64(binary.BigEndian.Uint64(value[:8])) 178 e.Data, err = o.MockStore.Get(keyItem.Address) 179 return e, err 180 } 181 } else { 182 encodeValueFunc = func(fields shed.Item) (value []byte, err error) { 183 b := make([]byte, 8) 184 binary.BigEndian.PutUint64(b, uint64(fields.StoreTimestamp)) 185 value = append(b, fields.Data...) 186 return value, nil 187 } 188 decodeValueFunc = func(keyItem shed.Item, value []byte) (e shed.Item, err error) { 189 e.StoreTimestamp = int64(binary.BigEndian.Uint64(value[:8])) 190 e.Data = value[8:] 191 return e, nil 192 } 193 } 194 // Index storing actual chunk address, data and store timestamp. 195 db.retrievalDataIndex, err = db.shed.NewIndex("Address->StoreTimestamp|Data", shed.IndexFuncs{ 196 EncodeKey: func(fields shed.Item) (key []byte, err error) { 197 return fields.Address, nil 198 }, 199 DecodeKey: func(key []byte) (e shed.Item, err error) { 200 e.Address = key 201 return e, nil 202 }, 203 EncodeValue: encodeValueFunc, 204 DecodeValue: decodeValueFunc, 205 }) 206 if err != nil { 207 return nil, err 208 } 209 // Index storing access timestamp for a particular address. 210 // It is needed in order to update gc index keys for iteration order. 211 db.retrievalAccessIndex, err = db.shed.NewIndex("Address->AccessTimestamp", shed.IndexFuncs{ 212 EncodeKey: func(fields shed.Item) (key []byte, err error) { 213 return fields.Address, nil 214 }, 215 DecodeKey: func(key []byte) (e shed.Item, err error) { 216 e.Address = key 217 return e, nil 218 }, 219 EncodeValue: func(fields shed.Item) (value []byte, err error) { 220 b := make([]byte, 8) 221 binary.BigEndian.PutUint64(b, uint64(fields.AccessTimestamp)) 222 return b, nil 223 }, 224 DecodeValue: func(keyItem shed.Item, value []byte) (e shed.Item, err error) { 225 e.AccessTimestamp = int64(binary.BigEndian.Uint64(value)) 226 return e, nil 227 }, 228 }) 229 if err != nil { 230 return nil, err 231 } 232 // pull index allows history and live syncing per po bin 233 db.pullIndex, err = db.shed.NewIndex("PO|StoredTimestamp|Hash->nil", shed.IndexFuncs{ 234 EncodeKey: func(fields shed.Item) (key []byte, err error) { 235 key = make([]byte, 41) 236 key[0] = db.po(fields.Address) 237 binary.BigEndian.PutUint64(key[1:9], uint64(fields.StoreTimestamp)) 238 copy(key[9:], fields.Address[:]) 239 return key, nil 240 }, 241 DecodeKey: func(key []byte) (e shed.Item, err error) { 242 e.Address = key[9:] 243 e.StoreTimestamp = int64(binary.BigEndian.Uint64(key[1:9])) 244 return e, nil 245 }, 246 EncodeValue: func(fields shed.Item) (value []byte, err error) { 247 return nil, nil 248 }, 249 DecodeValue: func(keyItem shed.Item, value []byte) (e shed.Item, err error) { 250 return e, nil 251 }, 252 }) 253 if err != nil { 254 return nil, err 255 } 256 // create a pull syncing triggers used by SubscribePull function 257 db.pullTriggers = make(map[uint8][]chan struct{}) 258 // push index contains as yet unsynced chunks 259 db.pushIndex, err = db.shed.NewIndex("StoredTimestamp|Hash->nil", shed.IndexFuncs{ 260 EncodeKey: func(fields shed.Item) (key []byte, err error) { 261 key = make([]byte, 40) 262 binary.BigEndian.PutUint64(key[:8], uint64(fields.StoreTimestamp)) 263 copy(key[8:], fields.Address[:]) 264 return key, nil 265 }, 266 DecodeKey: func(key []byte) (e shed.Item, err error) { 267 e.Address = key[8:] 268 e.StoreTimestamp = int64(binary.BigEndian.Uint64(key[:8])) 269 return e, nil 270 }, 271 EncodeValue: func(fields shed.Item) (value []byte, err error) { 272 return nil, nil 273 }, 274 DecodeValue: func(keyItem shed.Item, value []byte) (e shed.Item, err error) { 275 return e, nil 276 }, 277 }) 278 if err != nil { 279 return nil, err 280 } 281 // create a push syncing triggers used by SubscribePush function 282 db.pushTriggers = make([]chan struct{}, 0) 283 // gc index for removable chunk ordered by ascending last access time 284 db.gcIndex, err = db.shed.NewIndex("AccessTimestamp|StoredTimestamp|Hash->nil", shed.IndexFuncs{ 285 EncodeKey: func(fields shed.Item) (key []byte, err error) { 286 b := make([]byte, 16, 16+len(fields.Address)) 287 binary.BigEndian.PutUint64(b[:8], uint64(fields.AccessTimestamp)) 288 binary.BigEndian.PutUint64(b[8:16], uint64(fields.StoreTimestamp)) 289 key = append(b, fields.Address...) 290 return key, nil 291 }, 292 DecodeKey: func(key []byte) (e shed.Item, err error) { 293 e.AccessTimestamp = int64(binary.BigEndian.Uint64(key[:8])) 294 e.StoreTimestamp = int64(binary.BigEndian.Uint64(key[8:16])) 295 e.Address = key[16:] 296 return e, nil 297 }, 298 EncodeValue: func(fields shed.Item) (value []byte, err error) { 299 return nil, nil 300 }, 301 DecodeValue: func(keyItem shed.Item, value []byte) (e shed.Item, err error) { 302 return e, nil 303 }, 304 }) 305 if err != nil { 306 return nil, err 307 } 308 // start garbage collection worker 309 go db.collectGarbageWorker() 310 return db, nil 311 } 312 313 // Close closes the underlying database. 314 func (db *DB) Close() (err error) { 315 close(db.close) 316 db.updateGCWG.Wait() 317 318 // wait for gc worker to 319 // return before closing the shed 320 select { 321 case <-db.collectGarbageWorkerDone: 322 case <-time.After(5 * time.Second): 323 log.Error("localstore: collect garbage worker did not return after db close") 324 } 325 return db.shed.Close() 326 } 327 328 // po computes the proximity order between the address 329 // and database base key. 330 func (db *DB) po(addr chunk.Address) (bin uint8) { 331 return uint8(chunk.Proximity(db.baseKey, addr)) 332 } 333 334 // chunkToItem creates new Item with data provided by the Chunk. 335 func chunkToItem(ch chunk.Chunk) shed.Item { 336 return shed.Item{ 337 Address: ch.Address(), 338 Data: ch.Data(), 339 } 340 } 341 342 // addressToItem creates new Item with a provided address. 343 func addressToItem(addr chunk.Address) shed.Item { 344 return shed.Item{ 345 Address: addr, 346 } 347 } 348 349 // now is a helper function that returns a current unix timestamp 350 // in UTC timezone. 351 // It is set in the init function for usage in production, and 352 // optionally overridden in tests for data validation. 353 var now func() int64 354 355 func init() { 356 // set the now function 357 now = func() (t int64) { 358 return time.Now().UTC().UnixNano() 359 } 360 }