add mutex to stores

This commit is contained in:
zelig 2015-02-04 22:42:47 +01:00
parent 60931dfaa3
commit 05d636d47e
2 changed files with 389 additions and 371 deletions

View file

@ -1,371 +1,381 @@
// disk storage layer for the package blockhash // disk storage layer for the package blockhash
// inefficient work-in-progress version // inefficient work-in-progress version
package bzz package bzz
import ( import (
// "crypto/sha256" "bytes"
"bytes" "encoding/binary"
"encoding/binary" "sync"
"github.com/ethereum/go-ethereum/ethdb"
// "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
// "path" )
)
const dbMaxEntries = 5000 // max number of stored (cached) blocks
const dbMaxEntries = 5000 // max number of stored (cached) blocks
const gcArraySize = 500
const gcArraySize = 500 const gcArrayFreeRatio = 10
const gcArrayFreeRatio = 10
// key prefixes for leveldb storage
// key prefixes for leveldb storage const kpIndex = 0
const kpIndex = 0 const kpData = 1
const kpData = 1
var keyAccessCnt = []byte{2}
var keyAccessCnt = []byte{2} var keyEntryCnt = []byte{3}
var keyEntryCnt = []byte{3} var keyDataIdx = []byte{4}
var keyDataIdx = []byte{4} var keyGCPos = []byte{5}
var keyGCPos = []byte{5}
type gcItem struct {
type gcItem struct { idx uint64
idx uint64 value uint64
value uint64 idxKey []byte
idxKey []byte }
}
type dbStore struct {
type dbStore struct { db *ethdb.LDBDatabase
db *ethdb.LDBDatabase
// this should be stored in db, accessed transactionally
// this should be stored in db, accessed transactionally entryCnt, accessCnt, dataIdx uint64
entryCnt, accessCnt, dataIdx uint64
gcPos, gcStartPos []byte
gcPos, gcStartPos []byte gcArray []*gcItem
gcArray []*gcItem
} lock sync.Mutex
}
type dpaDBIndex struct {
Idx uint64 type dpaDBIndex struct {
Access uint64 Idx uint64
} Access uint64
}
func bytesToU64(data []byte) uint64 {
func bytesToU64(data []byte) uint64 {
if len(data) < 8 {
return 0 if len(data) < 8 {
} return 0
return binary.LittleEndian.Uint64(data) }
return binary.LittleEndian.Uint64(data)
}
}
func u64ToBytes(val uint64) []byte {
func u64ToBytes(val uint64) []byte {
data := make([]byte, 8)
binary.LittleEndian.PutUint64(data, val) data := make([]byte, 8)
return data binary.LittleEndian.PutUint64(data, val)
return data
}
}
func getIndexGCValue(index *dpaDBIndex) uint64 {
func getIndexGCValue(index *dpaDBIndex) uint64 {
return index.Access
return index.Access
}
}
func (s *dbStore) updateIndexAccess(index *dpaDBIndex) {
func (s *dbStore) updateIndexAccess(index *dpaDBIndex) {
index.Access = s.accessCnt
index.Access = s.accessCnt
}
}
func getIndexKey(hash Key) []byte {
func getIndexKey(hash Key) []byte {
HashSize := len(hash)
key := make([]byte, HashSize+1) HashSize := len(hash)
key[0] = 0 key := make([]byte, HashSize+1)
// db keys derived from hash: key[0] = 0
// two halves swapped for uniformly distributed prefix // db keys derived from hash:
copy(key[1:HashSize/2+1], hash[HashSize/2:HashSize]) // two halves swapped for uniformly distributed prefix
copy(key[HashSize/2+1:HashSize+1], hash[0:HashSize/2]) copy(key[1:HashSize/2+1], hash[HashSize/2:HashSize])
copy(key[HashSize/2+1:HashSize+1], hash[0:HashSize/2])
return key
} return key
}
func getDataKey(idx uint64) []byte {
func getDataKey(idx uint64) []byte {
key := make([]byte, 9)
key[0] = 1 key := make([]byte, 9)
binary.BigEndian.PutUint64(key[1:9], idx) key[0] = 1
binary.BigEndian.PutUint64(key[1:9], idx)
return key
} return key
}
func encodeIndex(index *dpaDBIndex) []byte {
func encodeIndex(index *dpaDBIndex) []byte {
data, _ := rlp.EncodeToBytes(index)
return data data, _ := rlp.EncodeToBytes(index)
return data
}
}
func encodeData(chunk *Chunk) []byte {
func encodeData(chunk *Chunk) []byte {
var rlpEntry struct {
Data []byte var rlpEntry struct {
Size uint64 Data []byte
} Size uint64
}
rlpEntry.Data = chunk.Data
rlpEntry.Size = uint64(chunk.Size) rlpEntry.Data = chunk.Data
rlpEntry.Size = uint64(chunk.Size)
data, _ := rlp.EncodeToBytes(rlpEntry)
return data data, _ := rlp.EncodeToBytes(rlpEntry)
return data
}
}
func decodeIndex(data []byte, index *dpaDBIndex) {
func decodeIndex(data []byte, index *dpaDBIndex) {
dec := rlp.NewStream(bytes.NewReader(data))
dec.Decode(index) dec := rlp.NewStream(bytes.NewReader(data))
dec.Decode(index)
}
}
func decodeData(data []byte, chunk *Chunk) {
func decodeData(data []byte, chunk *Chunk) {
var rlpEntry struct {
Data []byte var rlpEntry struct {
Size uint64 Data []byte
} Size uint64
}
dec := rlp.NewStream(bytes.NewReader(data))
err := dec.Decode(&rlpEntry) dec := rlp.NewStream(bytes.NewReader(data))
if err != nil { err := dec.Decode(&rlpEntry)
panic(err.Error()) if err != nil {
} panic(err.Error())
chunk.Data = rlpEntry.Data }
chunk.Size = int64(rlpEntry.Size) chunk.Data = rlpEntry.Data
} chunk.Size = int64(rlpEntry.Size)
}
func gcListPartition(list []*gcItem, left int, right int, pivotIndex int) int {
pivotValue := list[pivotIndex].value func gcListPartition(list []*gcItem, left int, right int, pivotIndex int) int {
dd := list[pivotIndex] pivotValue := list[pivotIndex].value
list[pivotIndex] = list[right] dd := list[pivotIndex]
list[right] = dd list[pivotIndex] = list[right]
storeIndex := left list[right] = dd
for i := left; i < right; i++ { storeIndex := left
if list[i].value < pivotValue { for i := left; i < right; i++ {
dd = list[storeIndex] if list[i].value < pivotValue {
list[storeIndex] = list[i] dd = list[storeIndex]
list[i] = dd list[storeIndex] = list[i]
storeIndex++ list[i] = dd
} storeIndex++
} }
dd = list[storeIndex] }
list[storeIndex] = list[right] dd = list[storeIndex]
list[right] = dd list[storeIndex] = list[right]
return storeIndex list[right] = dd
} return storeIndex
}
func gcListSelect(list []*gcItem, left int, right int, n int) int {
if left == right { func gcListSelect(list []*gcItem, left int, right int, n int) int {
return left if left == right {
} return left
pivotIndex := (left + right) / 2 }
pivotIndex = gcListPartition(list, left, right, pivotIndex) pivotIndex := (left + right) / 2
if n == pivotIndex { pivotIndex = gcListPartition(list, left, right, pivotIndex)
return n if n == pivotIndex {
} else { return n
if n < pivotIndex { } else {
return gcListSelect(list, left, pivotIndex-1, n) if n < pivotIndex {
} else { return gcListSelect(list, left, pivotIndex-1, n)
return gcListSelect(list, pivotIndex+1, right, n) } else {
} return gcListSelect(list, pivotIndex+1, right, n)
} }
} }
}
func (s *dbStore) collectGarbage() {
func (s *dbStore) collectGarbage() {
it := s.db.NewIterator()
it.Seek(s.gcPos) it := s.db.NewIterator()
if it.Valid() { it.Seek(s.gcPos)
s.gcPos = it.Key() if it.Valid() {
} else { s.gcPos = it.Key()
s.gcPos = nil } else {
} s.gcPos = nil
gcnt := 0 }
gcnt := 0
for gcnt < gcArraySize {
for gcnt < gcArraySize {
if (s.gcPos == nil) || (s.gcPos[0] != kpIndex) {
it.Seek(s.gcStartPos) if (s.gcPos == nil) || (s.gcPos[0] != kpIndex) {
if it.Valid() { it.Seek(s.gcStartPos)
s.gcPos = it.Key() if it.Valid() {
} else { s.gcPos = it.Key()
s.gcPos = nil } else {
} s.gcPos = nil
} }
}
if (s.gcPos == nil) || (s.gcPos[0] != kpIndex) {
break if (s.gcPos == nil) || (s.gcPos[0] != kpIndex) {
} break
}
gci := new(gcItem)
gci.idxKey = s.gcPos gci := new(gcItem)
var index dpaDBIndex gci.idxKey = s.gcPos
decodeIndex(it.Value(), &index) var index dpaDBIndex
gci.idx = index.Idx decodeIndex(it.Value(), &index)
// the smaller, the more likely to be gc'd gci.idx = index.Idx
gci.value = getIndexGCValue(&index) // the smaller, the more likely to be gc'd
s.gcArray[gcnt] = gci gci.value = getIndexGCValue(&index)
gcnt++ s.gcArray[gcnt] = gci
it.Next() gcnt++
if it.Valid() { it.Next()
s.gcPos = it.Key() if it.Valid() {
} else { s.gcPos = it.Key()
s.gcPos = nil } else {
} s.gcPos = nil
} }
}
cutidx := gcListSelect(s.gcArray, 0, gcnt-1, gcnt/gcArrayFreeRatio)
cutval := s.gcArray[cutidx].value cutidx := gcListSelect(s.gcArray, 0, gcnt-1, gcnt/gcArrayFreeRatio)
cutval := s.gcArray[cutidx].value
//fmt.Print(s.entryCnt, " ")
//fmt.Print(s.entryCnt, " ")
// actual gc
for i := 0; i < gcnt; i++ { // actual gc
if s.gcArray[i].value < cutval { for i := 0; i < gcnt; i++ {
batch := new(leveldb.Batch) if s.gcArray[i].value < cutval {
batch.Delete(s.gcArray[i].idxKey) batch := new(leveldb.Batch)
batch.Delete(getDataKey(s.gcArray[i].idx)) batch.Delete(s.gcArray[i].idxKey)
s.entryCnt-- batch.Delete(getDataKey(s.gcArray[i].idx))
batch.Put(keyEntryCnt, u64ToBytes(s.entryCnt)) s.entryCnt--
s.db.Write(batch) batch.Put(keyEntryCnt, u64ToBytes(s.entryCnt))
} s.db.Write(batch)
} }
}
//fmt.Println(s.entryCnt)
//fmt.Println(s.entryCnt)
s.db.Put(keyGCPos, s.gcPos)
s.db.Put(keyGCPos, s.gcPos)
}
}
func (s *dbStore) Put(chunk *Chunk) {
func (s *dbStore) Put(chunk *Chunk) {
ikey := getIndexKey(chunk.Key)
var index dpaDBIndex s.lock.Lock()
defer s.lock.Unlock()
if s.tryAccessIdx(ikey, &index) {
return // already exists, only update access ikey := getIndexKey(chunk.Key)
} var index dpaDBIndex
data := encodeData(chunk) if s.tryAccessIdx(ikey, &index) {
//data := ethutil.Encode([]interface{}{entry}) return // already exists, only update access
}
if s.entryCnt >= dbMaxEntries {
s.collectGarbage() data := encodeData(chunk)
} //data := ethutil.Encode([]interface{}{entry})
batch := new(leveldb.Batch) if s.entryCnt >= dbMaxEntries {
s.collectGarbage()
s.entryCnt++ }
batch.Put(keyEntryCnt, u64ToBytes(s.entryCnt))
s.dataIdx++ batch := new(leveldb.Batch)
batch.Put(keyDataIdx, u64ToBytes(s.dataIdx))
s.accessCnt++ s.entryCnt++
batch.Put(keyAccessCnt, u64ToBytes(s.accessCnt)) batch.Put(keyEntryCnt, u64ToBytes(s.entryCnt))
s.dataIdx++
batch.Put(getDataKey(s.dataIdx), data) batch.Put(keyDataIdx, u64ToBytes(s.dataIdx))
s.accessCnt++
index.Idx = s.dataIdx batch.Put(keyAccessCnt, u64ToBytes(s.accessCnt))
s.updateIndexAccess(&index)
batch.Put(getDataKey(s.dataIdx), data)
idata := encodeIndex(&index)
batch.Put(ikey, idata) index.Idx = s.dataIdx
s.updateIndexAccess(&index)
s.db.Write(batch)
idata := encodeIndex(&index)
} batch.Put(ikey, idata)
// try to find index; if found, update access cnt and return true s.db.Write(batch)
func (s *dbStore) tryAccessIdx(ikey []byte, index *dpaDBIndex) bool {
}
idata, err := s.db.Get(ikey)
if err != nil { // try to find index; if found, update access cnt and return true
return false func (s *dbStore) tryAccessIdx(ikey []byte, index *dpaDBIndex) bool {
}
decodeIndex(idata, index) idata, err := s.db.Get(ikey)
if err != nil {
batch := new(leveldb.Batch) return false
}
s.accessCnt++ decodeIndex(idata, index)
batch.Put(keyAccessCnt, u64ToBytes(s.accessCnt))
s.updateIndexAccess(index) batch := new(leveldb.Batch)
idata = encodeIndex(index)
batch.Put(ikey, idata) s.accessCnt++
batch.Put(keyAccessCnt, u64ToBytes(s.accessCnt))
s.db.Write(batch) s.updateIndexAccess(index)
idata = encodeIndex(index)
return true batch.Put(ikey, idata)
}
s.db.Write(batch)
func (s *dbStore) Get(key Key) (chunk *Chunk, err error) {
return true
var index dpaDBIndex }
if s.tryAccessIdx(getIndexKey(key), &index) { func (s *dbStore) Get(key Key) (chunk *Chunk, err error) {
var data []byte
data, err = s.db.Get(getDataKey(index.Idx)) s.lock.Lock()
if err != nil { defer s.lock.Unlock()
return
} var index dpaDBIndex
chunk = &Chunk{
Key: key, if s.tryAccessIdx(getIndexKey(key), &index) {
} var data []byte
decodeData(data, chunk) data, err = s.db.Get(getDataKey(index.Idx))
} else { if err != nil {
err = notFound return
} }
chunk = &Chunk{
return Key: key,
}
} decodeData(data, chunk)
} else {
func (s *dbStore) updateAccessCnt(key Key) { err = notFound
}
var index dpaDBIndex
s.tryAccessIdx(getIndexKey(key), &index) // result_chn == nil, only update access cnt return
} }
func newDbStore(path string) (s *dbStore, err error) { func (s *dbStore) updateAccessCnt(key Key) {
s = new(dbStore) s.lock.Lock()
defer s.lock.Unlock()
s.db, err = ethdb.NewLDBDatabase(path)
if err != nil { var index dpaDBIndex
return s.tryAccessIdx(getIndexKey(key), &index) // result_chn == nil, only update access cnt
}
}
s.gcStartPos = make([]byte, 1)
s.gcStartPos[0] = kpIndex func newDbStore(path string) (s *dbStore, err error) {
s.gcArray = make([]*gcItem, gcArraySize)
s = new(dbStore)
data, _ := s.db.Get(keyEntryCnt)
s.entryCnt = bytesToU64(data) s.db, err = ethdb.NewLDBDatabase(path)
data, _ = s.db.Get(keyAccessCnt) if err != nil {
s.accessCnt = bytesToU64(data) return
data, _ = s.db.Get(keyDataIdx) }
s.dataIdx = bytesToU64(data)
s.gcPos, _ = s.db.Get(keyGCPos) s.gcStartPos = make([]byte, 1)
if s.gcPos == nil { s.gcStartPos[0] = kpIndex
s.gcPos = s.gcStartPos s.gcArray = make([]*gcItem, gcArraySize)
}
return data, _ := s.db.Get(keyEntryCnt)
// fmt.Println(s.entryCnt) s.entryCnt = bytesToU64(data)
// fmt.Println(s.accessCnt) data, _ = s.db.Get(keyAccessCnt)
// fmt.Println(s.dataIdx) s.accessCnt = bytesToU64(data)
data, _ = s.db.Get(keyDataIdx)
} s.dataIdx = bytesToU64(data)
s.gcPos, _ = s.db.Get(keyGCPos)
func (s *dbStore) close() { if s.gcPos == nil {
s.db.Close() s.gcPos = s.gcStartPos
} }
return
// fmt.Println(s.entryCnt)
// fmt.Println(s.accessCnt)
// fmt.Println(s.dataIdx)
}
func (s *dbStore) close() {
s.db.Close()
}

View file

@ -4,6 +4,7 @@ package bzz
import ( import (
"bytes" "bytes"
"sync"
) )
const ( const (
@ -19,6 +20,7 @@ type memStore struct {
accessCnt uint64 // access counter; oldest is thrown away when full accessCnt uint64 // access counter; oldest is thrown away when full
dbAccessCnt uint64 dbAccessCnt uint64
dbStore *dbStore dbStore *dbStore
lock sync.Mutex
} }
/* /*
@ -141,6 +143,9 @@ func (node *memTree) updateAccess(a uint64) {
func (s *memStore) Put(entry *Chunk) { func (s *memStore) Put(entry *Chunk) {
s.lock.Lock()
defer s.lock.Unlock()
if s.entryCnt >= maxEntries { if s.entryCnt >= maxEntries {
s.removeOldest() s.removeOldest()
} }
@ -201,6 +206,9 @@ func (s *memStore) Put(entry *Chunk) {
func (s *memStore) Get(hash Key) (chunk *Chunk, err error) { func (s *memStore) Get(hash Key) (chunk *Chunk, err error) {
s.lock.Lock()
defer s.lock.Unlock()
node := s.memtree node := s.memtree
bitpos := uint(0) bitpos := uint(0)
for node.entry == nil { for node.entry == nil {