github.com/murrekatt/go-ethereum@v1.5.8-0.20170123175102-fc52f2c007fb/swarm/storage/dbstore.go (about) 1 // Copyright 2016 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 // disk storage layer for the package bzz 18 // DbStore implements the ChunkStore interface and is used by the DPA as 19 // persistent storage of chunks 20 // it implements purging based on access count allowing for external control of 21 // max capacity 22 23 package storage 24 25 import ( 26 "bytes" 27 "encoding/binary" 28 "fmt" 29 "sync" 30 31 "github.com/ethereum/go-ethereum/logger" 32 "github.com/ethereum/go-ethereum/logger/glog" 33 "github.com/ethereum/go-ethereum/rlp" 34 "github.com/syndtr/goleveldb/leveldb" 35 "github.com/syndtr/goleveldb/leveldb/iterator" 36 ) 37 38 const ( 39 defaultDbCapacity = 5000000 40 defaultRadius = 0 // not yet used 41 42 gcArraySize = 10000 43 gcArrayFreeRatio = 0.1 44 45 // key prefixes for leveldb storage 46 kpIndex = 0 47 kpData = 1 48 ) 49 50 var ( 51 keyAccessCnt = []byte{2} 52 keyEntryCnt = []byte{3} 53 keyDataIdx = []byte{4} 54 keyGCPos = []byte{5} 55 ) 56 57 type gcItem struct { 58 idx uint64 59 value uint64 60 idxKey []byte 61 } 62 63 type DbStore struct { 64 db *LDBDatabase 65 66 // this should be stored in db, accessed transactionally 67 entryCnt, accessCnt, dataIdx, capacity uint64 68 69 gcPos, gcStartPos []byte 70 gcArray []*gcItem 71 72 hashfunc Hasher 73 74 lock sync.Mutex 75 } 76 77 func NewDbStore(path string, hash Hasher, capacity uint64, radius int) (s *DbStore, err error) { 78 s = new(DbStore) 79 80 s.hashfunc = hash 81 82 s.db, err = NewLDBDatabase(path) 83 if err != nil { 84 return 85 } 86 87 s.setCapacity(capacity) 88 89 s.gcStartPos = make([]byte, 1) 90 s.gcStartPos[0] = kpIndex 91 s.gcArray = make([]*gcItem, gcArraySize) 92 93 data, _ := s.db.Get(keyEntryCnt) 94 s.entryCnt = BytesToU64(data) 95 data, _ = s.db.Get(keyAccessCnt) 96 s.accessCnt = BytesToU64(data) 97 data, _ = s.db.Get(keyDataIdx) 98 s.dataIdx = BytesToU64(data) 99 s.gcPos, _ = s.db.Get(keyGCPos) 100 if s.gcPos == nil { 101 s.gcPos = s.gcStartPos 102 } 103 return 104 } 105 106 type dpaDBIndex struct { 107 Idx uint64 108 Access uint64 109 } 110 111 func BytesToU64(data []byte) uint64 { 112 if len(data) < 8 { 113 return 0 114 } 115 return binary.LittleEndian.Uint64(data) 116 } 117 118 func U64ToBytes(val uint64) []byte { 119 data := make([]byte, 8) 120 binary.LittleEndian.PutUint64(data, val) 121 return data 122 } 123 124 func getIndexGCValue(index *dpaDBIndex) uint64 { 125 return index.Access 126 } 127 128 func (s *DbStore) updateIndexAccess(index *dpaDBIndex) { 129 index.Access = s.accessCnt 130 } 131 132 func getIndexKey(hash Key) []byte { 133 HashSize := len(hash) 134 key := make([]byte, HashSize+1) 135 key[0] = 0 136 copy(key[1:], hash[:]) 137 return key 138 } 139 140 func getDataKey(idx uint64) []byte { 141 key := make([]byte, 9) 142 key[0] = 1 143 binary.BigEndian.PutUint64(key[1:9], idx) 144 145 return key 146 } 147 148 func encodeIndex(index *dpaDBIndex) []byte { 149 data, _ := rlp.EncodeToBytes(index) 150 return data 151 } 152 153 func encodeData(chunk *Chunk) []byte { 154 return chunk.SData 155 } 156 157 func decodeIndex(data []byte, index *dpaDBIndex) { 158 dec := rlp.NewStream(bytes.NewReader(data), 0) 159 dec.Decode(index) 160 } 161 162 func decodeData(data []byte, chunk *Chunk) { 163 chunk.SData = data 164 chunk.Size = int64(binary.LittleEndian.Uint64(data[0:8])) 165 } 166 167 func gcListPartition(list []*gcItem, left int, right int, pivotIndex int) int { 168 pivotValue := list[pivotIndex].value 169 dd := list[pivotIndex] 170 list[pivotIndex] = list[right] 171 list[right] = dd 172 storeIndex := left 173 for i := left; i < right; i++ { 174 if list[i].value < pivotValue { 175 dd = list[storeIndex] 176 list[storeIndex] = list[i] 177 list[i] = dd 178 storeIndex++ 179 } 180 } 181 dd = list[storeIndex] 182 list[storeIndex] = list[right] 183 list[right] = dd 184 return storeIndex 185 } 186 187 func gcListSelect(list []*gcItem, left int, right int, n int) int { 188 if left == right { 189 return left 190 } 191 pivotIndex := (left + right) / 2 192 pivotIndex = gcListPartition(list, left, right, pivotIndex) 193 if n == pivotIndex { 194 return n 195 } else { 196 if n < pivotIndex { 197 return gcListSelect(list, left, pivotIndex-1, n) 198 } else { 199 return gcListSelect(list, pivotIndex+1, right, n) 200 } 201 } 202 } 203 204 func (s *DbStore) collectGarbage(ratio float32) { 205 it := s.db.NewIterator() 206 it.Seek(s.gcPos) 207 if it.Valid() { 208 s.gcPos = it.Key() 209 } else { 210 s.gcPos = nil 211 } 212 gcnt := 0 213 214 for (gcnt < gcArraySize) && (uint64(gcnt) < s.entryCnt) { 215 216 if (s.gcPos == nil) || (s.gcPos[0] != kpIndex) { 217 it.Seek(s.gcStartPos) 218 if it.Valid() { 219 s.gcPos = it.Key() 220 } else { 221 s.gcPos = nil 222 } 223 } 224 225 if (s.gcPos == nil) || (s.gcPos[0] != kpIndex) { 226 break 227 } 228 229 gci := new(gcItem) 230 gci.idxKey = s.gcPos 231 var index dpaDBIndex 232 decodeIndex(it.Value(), &index) 233 gci.idx = index.Idx 234 // the smaller, the more likely to be gc'd 235 gci.value = getIndexGCValue(&index) 236 s.gcArray[gcnt] = gci 237 gcnt++ 238 it.Next() 239 if it.Valid() { 240 s.gcPos = it.Key() 241 } else { 242 s.gcPos = nil 243 } 244 } 245 it.Release() 246 247 cutidx := gcListSelect(s.gcArray, 0, gcnt-1, int(float32(gcnt)*ratio)) 248 cutval := s.gcArray[cutidx].value 249 250 // fmt.Print(gcnt, " ", s.entryCnt, " ") 251 252 // actual gc 253 for i := 0; i < gcnt; i++ { 254 if s.gcArray[i].value <= cutval { 255 batch := new(leveldb.Batch) 256 batch.Delete(s.gcArray[i].idxKey) 257 batch.Delete(getDataKey(s.gcArray[i].idx)) 258 s.entryCnt-- 259 batch.Put(keyEntryCnt, U64ToBytes(s.entryCnt)) 260 s.db.Write(batch) 261 } 262 } 263 264 // fmt.Println(s.entryCnt) 265 266 s.db.Put(keyGCPos, s.gcPos) 267 } 268 269 func (s *DbStore) Counter() uint64 { 270 s.lock.Lock() 271 defer s.lock.Unlock() 272 return s.dataIdx 273 } 274 275 func (s *DbStore) Put(chunk *Chunk) { 276 s.lock.Lock() 277 defer s.lock.Unlock() 278 279 ikey := getIndexKey(chunk.Key) 280 var index dpaDBIndex 281 282 if s.tryAccessIdx(ikey, &index) { 283 if chunk.dbStored != nil { 284 close(chunk.dbStored) 285 } 286 return // already exists, only update access 287 } 288 289 data := encodeData(chunk) 290 //data := ethutil.Encode([]interface{}{entry}) 291 292 if s.entryCnt >= s.capacity { 293 s.collectGarbage(gcArrayFreeRatio) 294 } 295 296 batch := new(leveldb.Batch) 297 298 batch.Put(getDataKey(s.dataIdx), data) 299 300 index.Idx = s.dataIdx 301 s.updateIndexAccess(&index) 302 303 idata := encodeIndex(&index) 304 batch.Put(ikey, idata) 305 306 batch.Put(keyEntryCnt, U64ToBytes(s.entryCnt)) 307 s.entryCnt++ 308 batch.Put(keyDataIdx, U64ToBytes(s.dataIdx)) 309 s.dataIdx++ 310 batch.Put(keyAccessCnt, U64ToBytes(s.accessCnt)) 311 s.accessCnt++ 312 313 s.db.Write(batch) 314 if chunk.dbStored != nil { 315 close(chunk.dbStored) 316 } 317 glog.V(logger.Detail).Infof("DbStore.Put: %v. db storage counter: %v ", chunk.Key.Log(), s.dataIdx) 318 } 319 320 // try to find index; if found, update access cnt and return true 321 func (s *DbStore) tryAccessIdx(ikey []byte, index *dpaDBIndex) bool { 322 idata, err := s.db.Get(ikey) 323 if err != nil { 324 return false 325 } 326 decodeIndex(idata, index) 327 328 batch := new(leveldb.Batch) 329 330 batch.Put(keyAccessCnt, U64ToBytes(s.accessCnt)) 331 s.accessCnt++ 332 s.updateIndexAccess(index) 333 idata = encodeIndex(index) 334 batch.Put(ikey, idata) 335 336 s.db.Write(batch) 337 338 return true 339 } 340 341 func (s *DbStore) Get(key Key) (chunk *Chunk, err error) { 342 s.lock.Lock() 343 defer s.lock.Unlock() 344 345 var index dpaDBIndex 346 347 if s.tryAccessIdx(getIndexKey(key), &index) { 348 var data []byte 349 data, err = s.db.Get(getDataKey(index.Idx)) 350 if err != nil { 351 return 352 } 353 354 hasher := s.hashfunc() 355 hasher.Write(data) 356 hash := hasher.Sum(nil) 357 if !bytes.Equal(hash, key) { 358 s.db.Delete(getDataKey(index.Idx)) 359 err = fmt.Errorf("invalid chunk. hash=%x, key=%v", hash, key[:]) 360 return 361 } 362 363 chunk = &Chunk{ 364 Key: key, 365 } 366 decodeData(data, chunk) 367 } else { 368 err = notFound 369 } 370 371 return 372 373 } 374 375 func (s *DbStore) updateAccessCnt(key Key) { 376 377 s.lock.Lock() 378 defer s.lock.Unlock() 379 380 var index dpaDBIndex 381 s.tryAccessIdx(getIndexKey(key), &index) // result_chn == nil, only update access cnt 382 383 } 384 385 func (s *DbStore) setCapacity(c uint64) { 386 387 s.lock.Lock() 388 defer s.lock.Unlock() 389 390 s.capacity = c 391 392 if s.entryCnt > c { 393 var ratio float32 394 ratio = float32(1.01) - float32(c)/float32(s.entryCnt) 395 if ratio < gcArrayFreeRatio { 396 ratio = gcArrayFreeRatio 397 } 398 if ratio > 1 { 399 ratio = 1 400 } 401 for s.entryCnt > c { 402 s.collectGarbage(ratio) 403 } 404 } 405 } 406 407 func (s *DbStore) getEntryCnt() uint64 { 408 return s.entryCnt 409 } 410 411 func (s *DbStore) close() { 412 s.db.Close() 413 } 414 415 // describes a section of the DbStore representing the unsynced 416 // domain relevant to a peer 417 // Start - Stop designate a continuous area Keys in an address space 418 // typically the addresses closer to us than to the peer but not closer 419 // another closer peer in between 420 // From - To designates a time interval typically from the last disconnect 421 // till the latest connection (real time traffic is relayed) 422 type DbSyncState struct { 423 Start, Stop Key 424 First, Last uint64 425 } 426 427 // implements the syncer iterator interface 428 // iterates by storage index (~ time of storage = first entry to db) 429 type dbSyncIterator struct { 430 it iterator.Iterator 431 DbSyncState 432 } 433 434 // initialises a sync iterator from a syncToken (passed in with the handshake) 435 func (self *DbStore) NewSyncIterator(state DbSyncState) (si *dbSyncIterator, err error) { 436 if state.First > state.Last { 437 return nil, fmt.Errorf("no entries found") 438 } 439 si = &dbSyncIterator{ 440 it: self.db.NewIterator(), 441 DbSyncState: state, 442 } 443 si.it.Seek(getIndexKey(state.Start)) 444 return si, nil 445 } 446 447 // walk the area from Start to Stop and returns items within time interval 448 // First to Last 449 func (self *dbSyncIterator) Next() (key Key) { 450 for self.it.Valid() { 451 dbkey := self.it.Key() 452 if dbkey[0] != 0 { 453 break 454 } 455 key = Key(make([]byte, len(dbkey)-1)) 456 copy(key[:], dbkey[1:]) 457 if bytes.Compare(key[:], self.Start) <= 0 { 458 self.it.Next() 459 continue 460 } 461 if bytes.Compare(key[:], self.Stop) > 0 { 462 break 463 } 464 var index dpaDBIndex 465 decodeIndex(self.it.Value(), &index) 466 self.it.Next() 467 if (index.Idx >= self.First) && (index.Idx < self.Last) { 468 return 469 } 470 } 471 self.it.Release() 472 return nil 473 }