mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 18:02:24 +00:00
swarm/storage: split 3 impl for memstore
This commit is contained in:
parent
1d54933cd1
commit
cc0ea9dfe3
5 changed files with 524 additions and 372 deletions
|
|
@ -1,372 +0,0 @@
|
||||||
// Copyright 2016 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// memory storage layer for the package blockhash
|
|
||||||
|
|
||||||
package storage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
|
||||||
)
|
|
||||||
|
|
||||||
//metrics variables
|
|
||||||
var (
|
|
||||||
memstorePutCounter = metrics.NewRegisteredCounter("storage.db.memstore.put.count", nil)
|
|
||||||
memstoreRemoveCounter = metrics.NewRegisteredCounter("storage.db.memstore.rm.count", nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
memTreeLW = 2 // log2(subtree count) of the subtrees
|
|
||||||
memTreeFLW = 14 // log2(subtree count) of the root layer
|
|
||||||
dbForceUpdateAccessCnt = 1000
|
|
||||||
defaultCacheCapacity = 5000
|
|
||||||
)
|
|
||||||
|
|
||||||
//type MemStore struct {
|
|
||||||
//memtree *memTree
|
|
||||||
//entryCnt, capacity uint // stored entries
|
|
||||||
//accessCnt uint64 // access counter; oldest is thrown away when full
|
|
||||||
//dbAccessCnt uint64
|
|
||||||
//ldbStore *LDBStore
|
|
||||||
//lock sync.Mutex
|
|
||||||
//}
|
|
||||||
|
|
||||||
/*
|
|
||||||
a hash prefix subtree containing subtrees or one storage entry (but never both)
|
|
||||||
|
|
||||||
- access[0] stores the smallest (oldest) access count value in this subtree
|
|
||||||
- if it contains more subtrees and its subtree count is at least 4, access[1:2]
|
|
||||||
stores the smallest access count in the first and second halves of subtrees
|
|
||||||
(so that access[0] = min(access[1], access[2])
|
|
||||||
- likewise, if subtree count is at least 8,
|
|
||||||
access[1] = min(access[3], access[4])
|
|
||||||
access[2] = min(access[5], access[6])
|
|
||||||
(access[] is a binary tree inside the multi-bit leveled hash tree)
|
|
||||||
*/
|
|
||||||
|
|
||||||
//func NewMemStore(d *LDBStore, capacity uint) (m *MemStore) {
|
|
||||||
//m = &MemStore{}
|
|
||||||
//m.memtree = newMemTree(memTreeFLW, nil, 0)
|
|
||||||
//m.ldbStore = d
|
|
||||||
//m.setCapacity(capacity)
|
|
||||||
//return
|
|
||||||
//}
|
|
||||||
|
|
||||||
//type memTree struct {
|
|
||||||
//subtree []*memTree
|
|
||||||
//parent *memTree
|
|
||||||
//parentIdx uint
|
|
||||||
|
|
||||||
//bits uint // log2(subtree count)
|
|
||||||
//width uint // subtree count
|
|
||||||
|
|
||||||
//entry *Chunk // if subtrees are present, entry should be nil
|
|
||||||
//lastDBaccess uint64
|
|
||||||
//access []uint64
|
|
||||||
//}
|
|
||||||
|
|
||||||
//func newMemTree(b uint, parent *memTree, pidx uint) (node *memTree) {
|
|
||||||
//node = new(memTree)
|
|
||||||
//node.bits = b
|
|
||||||
//node.width = 1 << b
|
|
||||||
//node.subtree = make([]*memTree, node.width)
|
|
||||||
//node.access = make([]uint64, node.width-1)
|
|
||||||
//node.parent = parent
|
|
||||||
//node.parentIdx = pidx
|
|
||||||
//if parent != nil {
|
|
||||||
//parent.subtree[pidx] = node
|
|
||||||
//}
|
|
||||||
|
|
||||||
//return node
|
|
||||||
//}
|
|
||||||
|
|
||||||
//func (node *memTree) updateAccess(a uint64) {
|
|
||||||
//aidx := uint(0)
|
|
||||||
//var aa uint64
|
|
||||||
//oa := node.access[0]
|
|
||||||
//for node.access[aidx] == oa {
|
|
||||||
//node.access[aidx] = a
|
|
||||||
//if aidx > 0 {
|
|
||||||
//aa = node.access[((aidx-1)^1)+1]
|
|
||||||
//aidx = (aidx - 1) >> 1
|
|
||||||
//} else {
|
|
||||||
//pidx := node.parentIdx
|
|
||||||
//node = node.parent
|
|
||||||
//if node == nil {
|
|
||||||
//return
|
|
||||||
//}
|
|
||||||
//nn := node.subtree[pidx^1]
|
|
||||||
//if nn != nil {
|
|
||||||
//aa = nn.access[0]
|
|
||||||
//} else {
|
|
||||||
//aa = 0
|
|
||||||
//}
|
|
||||||
//aidx = (node.width + pidx - 2) >> 1
|
|
||||||
//}
|
|
||||||
|
|
||||||
//if (aa != 0) && (aa < a) {
|
|
||||||
//a = aa
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
//func (s *MemStore) setCapacity(c uint) {
|
|
||||||
//s.lock.Lock()
|
|
||||||
//defer s.lock.Unlock()
|
|
||||||
|
|
||||||
//for c < s.entryCnt {
|
|
||||||
//s.removeOldest()
|
|
||||||
//}
|
|
||||||
//s.capacity = c
|
|
||||||
//}
|
|
||||||
|
|
||||||
//func (s *MemStore) Counter() uint {
|
|
||||||
//return s.entryCnt
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// entry (not its copy) is going to be in MemStore
|
|
||||||
//func (s *MemStore) Put(entry *Chunk) {
|
|
||||||
//log.Trace("memstore.put", "key", entry.Key)
|
|
||||||
//if s.capacity == 0 {
|
|
||||||
//return
|
|
||||||
//}
|
|
||||||
|
|
||||||
//s.lock.Lock()
|
|
||||||
//defer s.lock.Unlock()
|
|
||||||
|
|
||||||
//if s.entryCnt >= s.capacity {
|
|
||||||
//s.removeOldest()
|
|
||||||
//}
|
|
||||||
|
|
||||||
//s.accessCnt++
|
|
||||||
|
|
||||||
//memstorePutCounter.Inc(1)
|
|
||||||
|
|
||||||
//node := s.memtree
|
|
||||||
//bitpos := uint(0)
|
|
||||||
//for node.entry == nil {
|
|
||||||
//l := entry.Key.bits(bitpos, node.bits)
|
|
||||||
//st := node.subtree[l]
|
|
||||||
//if st == nil {
|
|
||||||
//st = newMemTree(memTreeLW, node, l)
|
|
||||||
//bitpos += node.bits
|
|
||||||
//node = st
|
|
||||||
//break
|
|
||||||
//}
|
|
||||||
//bitpos += node.bits
|
|
||||||
//node = st
|
|
||||||
//}
|
|
||||||
|
|
||||||
//if node.entry != nil {
|
|
||||||
|
|
||||||
//if node.entry.Key.isEqual(entry.Key) {
|
|
||||||
//node.updateAccess(s.accessCnt)
|
|
||||||
//if entry.SData == nil {
|
|
||||||
//entry.Size = node.entry.Size
|
|
||||||
//entry.SData = node.entry.SData
|
|
||||||
//}
|
|
||||||
//if entry.ReqC == nil {
|
|
||||||
//entry.ReqC = node.entry.ReqC
|
|
||||||
//}
|
|
||||||
//entry.C = node.entry.C
|
|
||||||
//node.entry = entry
|
|
||||||
//return
|
|
||||||
//}
|
|
||||||
|
|
||||||
//for node.entry != nil {
|
|
||||||
|
|
||||||
//l := node.entry.Key.bits(bitpos, node.bits)
|
|
||||||
//st := node.subtree[l]
|
|
||||||
//if st == nil {
|
|
||||||
//st = newMemTree(memTreeLW, node, l)
|
|
||||||
//}
|
|
||||||
//st.entry = node.entry
|
|
||||||
//node.entry = nil
|
|
||||||
//st.updateAccess(node.access[0])
|
|
||||||
|
|
||||||
//l = entry.Key.bits(bitpos, node.bits)
|
|
||||||
//st = node.subtree[l]
|
|
||||||
//if st == nil {
|
|
||||||
//st = newMemTree(memTreeLW, node, l)
|
|
||||||
//}
|
|
||||||
//bitpos += node.bits
|
|
||||||
//node = st
|
|
||||||
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
//node.entry = entry
|
|
||||||
//node.lastDBaccess = s.dbAccessCnt
|
|
||||||
//node.updateAccess(s.accessCnt)
|
|
||||||
//s.entryCnt++
|
|
||||||
//}
|
|
||||||
|
|
||||||
//func (s *MemStore) Get(hash Key) (chunk *Chunk, err error) {
|
|
||||||
//log.Trace("memstore.get", "key", hash)
|
|
||||||
//s.lock.Lock()
|
|
||||||
//defer s.lock.Unlock()
|
|
||||||
|
|
||||||
//node := s.memtree
|
|
||||||
//bitpos := uint(0)
|
|
||||||
//for node.entry == nil {
|
|
||||||
//l := hash.bits(bitpos, node.bits)
|
|
||||||
//st := node.subtree[l]
|
|
||||||
//if st == nil {
|
|
||||||
//log.Trace("memstore.get ErrChunkNotFound", "key", hash)
|
|
||||||
//return nil, ErrChunkNotFound
|
|
||||||
//}
|
|
||||||
//bitpos += node.bits
|
|
||||||
//node = st
|
|
||||||
//}
|
|
||||||
|
|
||||||
//if node.entry.Key.isEqual(hash) {
|
|
||||||
//s.accessCnt++
|
|
||||||
//node.updateAccess(s.accessCnt)
|
|
||||||
//chunk = node.entry
|
|
||||||
//if s.dbAccessCnt-node.lastDBaccess > dbForceUpdateAccessCnt {
|
|
||||||
//s.dbAccessCnt++
|
|
||||||
//node.lastDBaccess = s.dbAccessCnt
|
|
||||||
//if s.ldbStore != nil {
|
|
||||||
//s.ldbStore.updateAccessCnt(hash)
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//} else {
|
|
||||||
//err = ErrChunkNotFound
|
|
||||||
//}
|
|
||||||
|
|
||||||
//log.Trace("memstore.get return", "key", hash, "chunk", chunk, "err", err)
|
|
||||||
//return
|
|
||||||
//}
|
|
||||||
|
|
||||||
//func (s *MemStore) removeOldest() {
|
|
||||||
//defer metrics.GetOrRegisterResettingTimer("memstore.purge", metrics.DefaultRegistry).UpdateSince(time.Now())
|
|
||||||
|
|
||||||
//node := s.memtree
|
|
||||||
//log.Warn("purge memstore")
|
|
||||||
//for node.entry == nil {
|
|
||||||
|
|
||||||
//aidx := uint(0)
|
|
||||||
//av := node.access[aidx]
|
|
||||||
|
|
||||||
//for aidx < node.width/2-1 {
|
|
||||||
//if av == node.access[aidx*2+1] {
|
|
||||||
//node.access[aidx] = node.access[aidx*2+2]
|
|
||||||
//aidx = aidx*2 + 1
|
|
||||||
//} else if av == node.access[aidx*2+2] {
|
|
||||||
//node.access[aidx] = node.access[aidx*2+1]
|
|
||||||
//aidx = aidx*2 + 2
|
|
||||||
//} else {
|
|
||||||
//panic(nil)
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//pidx := aidx*2 + 2 - node.width
|
|
||||||
//if (node.subtree[pidx] != nil) && (av == node.subtree[pidx].access[0]) {
|
|
||||||
//if node.subtree[pidx+1] != nil {
|
|
||||||
//node.access[aidx] = node.subtree[pidx+1].access[0]
|
|
||||||
//} else {
|
|
||||||
//node.access[aidx] = 0
|
|
||||||
//}
|
|
||||||
//} else if (node.subtree[pidx+1] != nil) && (av == node.subtree[pidx+1].access[0]) {
|
|
||||||
//if node.subtree[pidx] != nil {
|
|
||||||
//node.access[aidx] = node.subtree[pidx].access[0]
|
|
||||||
//} else {
|
|
||||||
//node.access[aidx] = 0
|
|
||||||
//}
|
|
||||||
//pidx++
|
|
||||||
//} else {
|
|
||||||
//panic(nil)
|
|
||||||
//}
|
|
||||||
|
|
||||||
////fmt.Println(pidx)
|
|
||||||
//node = node.subtree[pidx]
|
|
||||||
|
|
||||||
//}
|
|
||||||
|
|
||||||
//if node.entry.ReqC == nil {
|
|
||||||
//log.Trace(fmt.Sprintf("Memstore Clean: Waiting for chunk %v to be saved", node.entry.Key.Log()))
|
|
||||||
//<-node.entry.dbStoredC
|
|
||||||
//log.Trace(fmt.Sprintf("Memstore Clean: Chunk %v saved to DBStore. Ready to clear from mem.", node.entry.Key.Log()))
|
|
||||||
|
|
||||||
//memstoreRemoveCounter.Inc(1)
|
|
||||||
//node.entry = nil
|
|
||||||
//s.entryCnt--
|
|
||||||
//} else {
|
|
||||||
//return
|
|
||||||
//}
|
|
||||||
|
|
||||||
//node.access[0] = 0
|
|
||||||
|
|
||||||
////---
|
|
||||||
|
|
||||||
//aidx := uint(0)
|
|
||||||
//for {
|
|
||||||
//aa := node.access[aidx]
|
|
||||||
//if aidx > 0 {
|
|
||||||
//aidx = (aidx - 1) >> 1
|
|
||||||
//} else {
|
|
||||||
//pidx := node.parentIdx
|
|
||||||
//node = node.parent
|
|
||||||
//if node == nil {
|
|
||||||
//return
|
|
||||||
//}
|
|
||||||
//aidx = (node.width + pidx - 2) >> 1
|
|
||||||
//}
|
|
||||||
//if (aa != 0) && ((aa < node.access[aidx]) || (node.access[aidx] == 0)) {
|
|
||||||
//node.access[aidx] = aa
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
type MemStore struct {
|
|
||||||
m map[string]*Chunk
|
|
||||||
mu sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMemStore(d *LDBStore, capacity uint) (m *MemStore) {
|
|
||||||
return &MemStore{
|
|
||||||
m: make(map[string]*Chunk),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemStore) Get(key Key) (*Chunk, error) {
|
|
||||||
m.mu.RLock()
|
|
||||||
defer m.mu.RUnlock()
|
|
||||||
c, ok := m.m[string(key[:])]
|
|
||||||
if !ok {
|
|
||||||
return nil, ErrChunkNotFound
|
|
||||||
}
|
|
||||||
if !bytes.Equal(c.Key, key) {
|
|
||||||
panic(fmt.Errorf("MemStore.Get: chunk key %s != req key %s", c.Key.Hex(), key.Hex()))
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemStore) Put(c *Chunk) {
|
|
||||||
m.mu.Lock()
|
|
||||||
defer m.mu.Unlock()
|
|
||||||
m.m[string(c.Key[:])] = c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MemStore) setCapacity(n int) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close memstore
|
|
||||||
func (s *MemStore) Close() {}
|
|
||||||
74
swarm/storage/memstore_lrucache.go
Normal file
74
swarm/storage/memstore_lrucache.go
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// memory storage layer for the package blockhash
|
||||||
|
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
lru "github.com/hashicorp/golang-lru"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultCacheCapacity = 5000
|
||||||
|
)
|
||||||
|
|
||||||
|
type MemStore struct {
|
||||||
|
//cache *lru.ARCCache
|
||||||
|
cache *lru.Cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMemStore(_ *LDBStore, capacity uint) (m *MemStore) {
|
||||||
|
onEvicted := func(key interface{}, value interface{}) {
|
||||||
|
v := value.(*Chunk)
|
||||||
|
<-v.dbStoredC
|
||||||
|
}
|
||||||
|
c, err := lru.NewWithEvict(int(capacity), onEvicted)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &MemStore{
|
||||||
|
//cache: lru.NewARC(capacity),
|
||||||
|
cache: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MemStore) Get(key Key) (*Chunk, error) {
|
||||||
|
c, ok := m.cache.Get(string(key))
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrChunkNotFound
|
||||||
|
}
|
||||||
|
chunk := c.(*Chunk)
|
||||||
|
if !bytes.Equal(chunk.Key, key) {
|
||||||
|
panic(fmt.Errorf("MemStore.Get: chunk key %s != req key %s", chunk.Key.Hex(), key.Hex()))
|
||||||
|
}
|
||||||
|
return chunk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MemStore) Put(c *Chunk) {
|
||||||
|
m.cache.Add(string(c.Key), c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MemStore) setCapacity(n int) {
|
||||||
|
//no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close memstore
|
||||||
|
func (s *MemStore) Close() {}
|
||||||
77
swarm/storage/memstore_map.god
Normal file
77
swarm/storage/memstore_map.god
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright 2016 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// memory storage layer for the package blockhash
|
||||||
|
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
//metrics variables
|
||||||
|
var (
|
||||||
|
memstorePutCounter = metrics.NewRegisteredCounter("storage.db.memstore.put.count", nil)
|
||||||
|
memstoreRemoveCounter = metrics.NewRegisteredCounter("storage.db.memstore.rm.count", nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
memTreeLW = 2 // log2(subtree count) of the subtrees
|
||||||
|
memTreeFLW = 14 // log2(subtree count) of the root layer
|
||||||
|
dbForceUpdateAccessCnt = 1000
|
||||||
|
defaultCacheCapacity = 5000
|
||||||
|
)
|
||||||
|
|
||||||
|
type MemStore struct {
|
||||||
|
m map[string]*Chunk
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMemStore(d *LDBStore, capacity uint) (m *MemStore) {
|
||||||
|
return &MemStore{
|
||||||
|
m: make(map[string]*Chunk),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MemStore) Get(key Key) (*Chunk, error) {
|
||||||
|
m.mu.RLock()
|
||||||
|
defer m.mu.RUnlock()
|
||||||
|
c, ok := m.m[string(key[:])]
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrChunkNotFound
|
||||||
|
}
|
||||||
|
if !bytes.Equal(c.Key, key) {
|
||||||
|
panic(fmt.Errorf("MemStore.Get: chunk key %s != req key %s", c.Key.Hex(), key.Hex()))
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MemStore) Put(c *Chunk) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
m.m[string(c.Key[:])] = c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MemStore) setCapacity(n int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close memstore
|
||||||
|
func (s *MemStore) Close() {}
|
||||||
373
swarm/storage/memstore_tree.god
Normal file
373
swarm/storage/memstore_tree.god
Normal file
|
|
@ -0,0 +1,373 @@
|
||||||
|
// Copyright 2016 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// memory storage layer for the package blockhash
|
||||||
|
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
//metrics variables
|
||||||
|
var (
|
||||||
|
memstorePutCounter = metrics.NewRegisteredCounter("storage.db.memstore.put.count", nil)
|
||||||
|
memstoreRemoveCounter = metrics.NewRegisteredCounter("storage.db.memstore.rm.count", nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
memTreeLW = 2 // log2(subtree count) of the subtrees
|
||||||
|
memTreeFLW = 14 // log2(subtree count) of the root layer
|
||||||
|
dbForceUpdateAccessCnt = 1000
|
||||||
|
defaultCacheCapacity = 5000
|
||||||
|
)
|
||||||
|
|
||||||
|
type MemStore struct {
|
||||||
|
memtree *memTree
|
||||||
|
entryCnt, capacity uint // stored entries
|
||||||
|
accessCnt uint64 // access counter; oldest is thrown away when full
|
||||||
|
dbAccessCnt uint64
|
||||||
|
ldbStore *LDBStore
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
a hash prefix subtree containing subtrees or one storage entry (but never both)
|
||||||
|
|
||||||
|
- access[0] stores the smallest (oldest) access count value in this subtree
|
||||||
|
- if it contains more subtrees and its subtree count is at least 4, access[1:2]
|
||||||
|
stores the smallest access count in the first and second halves of subtrees
|
||||||
|
(so that access[0] = min(access[1], access[2])
|
||||||
|
- likewise, if subtree count is at least 8,
|
||||||
|
access[1] = min(access[3], access[4])
|
||||||
|
access[2] = min(access[5], access[6])
|
||||||
|
(access[] is a binary tree inside the multi-bit leveled hash tree)
|
||||||
|
*/
|
||||||
|
|
||||||
|
func NewMemStore(d *LDBStore, capacity uint) (m *MemStore) {
|
||||||
|
m = &MemStore{}
|
||||||
|
m.memtree = newMemTree(memTreeFLW, nil, 0)
|
||||||
|
m.ldbStore = d
|
||||||
|
m.setCapacity(capacity)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type memTree struct {
|
||||||
|
subtree []*memTree
|
||||||
|
parent *memTree
|
||||||
|
parentIdx uint
|
||||||
|
|
||||||
|
bits uint // log2(subtree count)
|
||||||
|
width uint // subtree count
|
||||||
|
|
||||||
|
entry *Chunk // if subtrees are present, entry should be nil
|
||||||
|
lastDBaccess uint64
|
||||||
|
access []uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMemTree(b uint, parent *memTree, pidx uint) (node *memTree) {
|
||||||
|
node = new(memTree)
|
||||||
|
node.bits = b
|
||||||
|
node.width = 1 << b
|
||||||
|
node.subtree = make([]*memTree, node.width)
|
||||||
|
node.access = make([]uint64, node.width-1)
|
||||||
|
node.parent = parent
|
||||||
|
node.parentIdx = pidx
|
||||||
|
if parent != nil {
|
||||||
|
parent.subtree[pidx] = node
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *memTree) updateAccess(a uint64) {
|
||||||
|
aidx := uint(0)
|
||||||
|
var aa uint64
|
||||||
|
oa := node.access[0]
|
||||||
|
for node.access[aidx] == oa {
|
||||||
|
node.access[aidx] = a
|
||||||
|
if aidx > 0 {
|
||||||
|
aa = node.access[((aidx-1)^1)+1]
|
||||||
|
aidx = (aidx - 1) >> 1
|
||||||
|
} else {
|
||||||
|
pidx := node.parentIdx
|
||||||
|
node = node.parent
|
||||||
|
if node == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nn := node.subtree[pidx^1]
|
||||||
|
if nn != nil {
|
||||||
|
aa = nn.access[0]
|
||||||
|
} else {
|
||||||
|
aa = 0
|
||||||
|
}
|
||||||
|
aidx = (node.width + pidx - 2) >> 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aa != 0) && (aa < a) {
|
||||||
|
a = aa
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MemStore) setCapacity(c uint) {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
|
for c < s.entryCnt {
|
||||||
|
s.removeOldest()
|
||||||
|
}
|
||||||
|
s.capacity = c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MemStore) Counter() uint {
|
||||||
|
return s.entryCnt
|
||||||
|
}
|
||||||
|
|
||||||
|
// entry (not its copy) is going to be in MemStore
|
||||||
|
func (s *MemStore) Put(entry *Chunk) {
|
||||||
|
log.Trace("memstore.put", "key", entry.Key)
|
||||||
|
if s.capacity == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
|
if s.entryCnt >= s.capacity {
|
||||||
|
s.removeOldest()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.accessCnt++
|
||||||
|
|
||||||
|
memstorePutCounter.Inc(1)
|
||||||
|
|
||||||
|
node := s.memtree
|
||||||
|
bitpos := uint(0)
|
||||||
|
for node.entry == nil {
|
||||||
|
l := entry.Key.bits(bitpos, node.bits)
|
||||||
|
st := node.subtree[l]
|
||||||
|
if st == nil {
|
||||||
|
st = newMemTree(memTreeLW, node, l)
|
||||||
|
bitpos += node.bits
|
||||||
|
node = st
|
||||||
|
break
|
||||||
|
}
|
||||||
|
bitpos += node.bits
|
||||||
|
node = st
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.entry != nil {
|
||||||
|
|
||||||
|
if node.entry.Key.isEqual(entry.Key) {
|
||||||
|
node.updateAccess(s.accessCnt)
|
||||||
|
if entry.SData == nil {
|
||||||
|
entry.Size = node.entry.Size
|
||||||
|
entry.SData = node.entry.SData
|
||||||
|
}
|
||||||
|
if entry.ReqC == nil {
|
||||||
|
entry.ReqC = node.entry.ReqC
|
||||||
|
}
|
||||||
|
entry.C = node.entry.C
|
||||||
|
node.entry = entry
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for node.entry != nil {
|
||||||
|
|
||||||
|
l := node.entry.Key.bits(bitpos, node.bits)
|
||||||
|
st := node.subtree[l]
|
||||||
|
if st == nil {
|
||||||
|
st = newMemTree(memTreeLW, node, l)
|
||||||
|
}
|
||||||
|
st.entry = node.entry
|
||||||
|
node.entry = nil
|
||||||
|
st.updateAccess(node.access[0])
|
||||||
|
|
||||||
|
l = entry.Key.bits(bitpos, node.bits)
|
||||||
|
st = node.subtree[l]
|
||||||
|
if st == nil {
|
||||||
|
st = newMemTree(memTreeLW, node, l)
|
||||||
|
}
|
||||||
|
bitpos += node.bits
|
||||||
|
node = st
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.entry = entry
|
||||||
|
node.lastDBaccess = s.dbAccessCnt
|
||||||
|
node.updateAccess(s.accessCnt)
|
||||||
|
s.entryCnt++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MemStore) Get(hash Key) (chunk *Chunk, err error) {
|
||||||
|
log.Trace("memstore.get", "key", hash)
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
|
node := s.memtree
|
||||||
|
bitpos := uint(0)
|
||||||
|
for node.entry == nil {
|
||||||
|
l := hash.bits(bitpos, node.bits)
|
||||||
|
st := node.subtree[l]
|
||||||
|
if st == nil {
|
||||||
|
log.Trace("memstore.get ErrChunkNotFound", "key", hash)
|
||||||
|
return nil, ErrChunkNotFound
|
||||||
|
}
|
||||||
|
bitpos += node.bits
|
||||||
|
node = st
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.entry.Key.isEqual(hash) {
|
||||||
|
s.accessCnt++
|
||||||
|
node.updateAccess(s.accessCnt)
|
||||||
|
chunk = node.entry
|
||||||
|
if s.dbAccessCnt-node.lastDBaccess > dbForceUpdateAccessCnt {
|
||||||
|
s.dbAccessCnt++
|
||||||
|
node.lastDBaccess = s.dbAccessCnt
|
||||||
|
if s.ldbStore != nil {
|
||||||
|
s.ldbStore.updateAccessCnt(hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = ErrChunkNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace("memstore.get return", "key", hash, "chunk", chunk, "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MemStore) removeOldest() {
|
||||||
|
defer metrics.GetOrRegisterResettingTimer("memstore.purge", metrics.DefaultRegistry).UpdateSince(time.Now())
|
||||||
|
|
||||||
|
node := s.memtree
|
||||||
|
log.Warn("purge memstore")
|
||||||
|
for node.entry == nil {
|
||||||
|
|
||||||
|
aidx := uint(0)
|
||||||
|
av := node.access[aidx]
|
||||||
|
|
||||||
|
for aidx < node.width/2-1 {
|
||||||
|
if av == node.access[aidx*2+1] {
|
||||||
|
node.access[aidx] = node.access[aidx*2+2]
|
||||||
|
aidx = aidx*2 + 1
|
||||||
|
} else if av == node.access[aidx*2+2] {
|
||||||
|
node.access[aidx] = node.access[aidx*2+1]
|
||||||
|
aidx = aidx*2 + 2
|
||||||
|
} else {
|
||||||
|
panic(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pidx := aidx*2 + 2 - node.width
|
||||||
|
if (node.subtree[pidx] != nil) && (av == node.subtree[pidx].access[0]) {
|
||||||
|
if node.subtree[pidx+1] != nil {
|
||||||
|
node.access[aidx] = node.subtree[pidx+1].access[0]
|
||||||
|
} else {
|
||||||
|
node.access[aidx] = 0
|
||||||
|
}
|
||||||
|
} else if (node.subtree[pidx+1] != nil) && (av == node.subtree[pidx+1].access[0]) {
|
||||||
|
if node.subtree[pidx] != nil {
|
||||||
|
node.access[aidx] = node.subtree[pidx].access[0]
|
||||||
|
} else {
|
||||||
|
node.access[aidx] = 0
|
||||||
|
}
|
||||||
|
pidx++
|
||||||
|
} else {
|
||||||
|
panic(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Println(pidx)
|
||||||
|
node = node.subtree[pidx]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.entry.ReqC == nil {
|
||||||
|
log.Trace(fmt.Sprintf("Memstore Clean: Waiting for chunk %v to be saved", node.entry.Key.Log()))
|
||||||
|
<-node.entry.dbStoredC
|
||||||
|
log.Trace(fmt.Sprintf("Memstore Clean: Chunk %v saved to DBStore. Ready to clear from mem.", node.entry.Key.Log()))
|
||||||
|
|
||||||
|
memstoreRemoveCounter.Inc(1)
|
||||||
|
node.entry = nil
|
||||||
|
s.entryCnt--
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
node.access[0] = 0
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
aidx := uint(0)
|
||||||
|
for {
|
||||||
|
aa := node.access[aidx]
|
||||||
|
if aidx > 0 {
|
||||||
|
aidx = (aidx - 1) >> 1
|
||||||
|
} else {
|
||||||
|
pidx := node.parentIdx
|
||||||
|
node = node.parent
|
||||||
|
if node == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
aidx = (node.width + pidx - 2) >> 1
|
||||||
|
}
|
||||||
|
if (aa != 0) && ((aa < node.access[aidx]) || (node.access[aidx] == 0)) {
|
||||||
|
node.access[aidx] = aa
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// type MemStore struct {
|
||||||
|
// m map[string]*Chunk
|
||||||
|
// mu sync.RWMutex
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func NewMemStore(d *DbStore, capacity uint) (m *MemStore) {
|
||||||
|
// return &MemStore{
|
||||||
|
// m: make(map[string]*Chunk),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (m *MemStore) Get(key Key) (*Chunk, error) {
|
||||||
|
// m.mu.RLock()
|
||||||
|
// defer m.mu.RUnlock()
|
||||||
|
// c, ok := m.m[string(key[:])]
|
||||||
|
// if !ok {
|
||||||
|
// return nil, ErrNotFound
|
||||||
|
// }
|
||||||
|
// if !bytes.Equal(c.Key, key) {
|
||||||
|
// panic(fmt.Errorf("MemStore.Get: chunk key %s != req key %s", c.Key.Hex(), key.Hex()))
|
||||||
|
// }
|
||||||
|
// return c, nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (m *MemStore) Put(c *Chunk) {
|
||||||
|
// m.mu.Lock()
|
||||||
|
// defer m.mu.Unlock()
|
||||||
|
// m.m[string(c.Key[:])] = c
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (m *MemStore) setCapacity(n int) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Close memstore
|
||||||
|
func (s *MemStore) Close() {}
|
||||||
Loading…
Reference in a new issue