diff --git a/core/rawdb/freezer.go b/core/rawdb/freezer.go index b7824ddc0d..bcac2cabf3 100644 --- a/core/rawdb/freezer.go +++ b/core/rawdb/freezer.go @@ -19,6 +19,7 @@ package rawdb import ( "errors" "fmt" + "github.com/ethereum/go-ethereum/hook" "math" "os" "path/filepath" @@ -186,7 +187,13 @@ func (f *Freezer) Close() error { // in the freezer. func (f *Freezer) HasAncient(kind string, number uint64) (bool, error) { if table := f.tables[kind]; table != nil { - return table.has(number), nil + if table.has(number) { + bytes, err := table.Retrieve(number) + if len(bytes) > 0 && err != nil { + hook.Gr.CountAncientDbRead(bytes) + } + return true, nil + } } return false, nil } @@ -194,7 +201,11 @@ func (f *Freezer) HasAncient(kind string, number uint64) (bool, error) { // Ancient retrieves an ancient binary blob from the append-only immutable files. func (f *Freezer) Ancient(kind string, number uint64) ([]byte, error) { if table := f.tables[kind]; table != nil { - return table.Retrieve(number) + bytes, err := table.Retrieve(number) + if len(bytes) > 0 && err != nil { + hook.Gr.CountAncientDbRead(bytes) + } + return bytes, err } return nil, errUnknownTable } diff --git a/ethdb/leveldb/leveldb.go b/ethdb/leveldb/leveldb.go index e58efbddbe..d3b8f6c74a 100644 --- a/ethdb/leveldb/leveldb.go +++ b/ethdb/leveldb/leveldb.go @@ -22,6 +22,7 @@ package leveldb import ( "fmt" + "github.com/ethereum/go-ethereum/hook" "strings" "sync" "time" @@ -185,11 +186,13 @@ func (db *Database) Close() error { // Has retrieves if a key is present in the key-value store. func (db *Database) Has(key []byte) (bool, error) { + hook.Gr.CountLevelDbRead(key) return db.db.Has(key, nil) } // Get retrieves the given key if it's present in the key-value store. func (db *Database) Get(key []byte) ([]byte, error) { + hook.Gr.CountLevelDbRead(key) dat, err := db.db.Get(key, nil) if err != nil { return nil, err @@ -469,12 +472,14 @@ type snapshot struct { // Has retrieves if a key is present in the snapshot backing by a key-value // data store. func (snap *snapshot) Has(key []byte) (bool, error) { + hook.Gr.CountLevelDbSnapshotRead(key) return snap.db.Has(key, nil) } // Get retrieves the given key if it's present in the snapshot backing by // key-value data store. func (snap *snapshot) Get(key []byte) ([]byte, error) { + hook.Gr.CountLevelDbSnapshotRead(key) return snap.db.Get(key, nil) } diff --git a/hook/record.go b/hook/record.go new file mode 100644 index 0000000000..f3949a0a8a --- /dev/null +++ b/hook/record.go @@ -0,0 +1,132 @@ +package hook + +import "sync" + +type Record struct { + ancientDbReadCnt int // ancient DB 접근 횟수 + levelDbReadCnt int // level DB 접근 횟수 + levelDbSnapshotReadCnt int // level DB 스냅샷 접근 횟수 + duplicatedReadCnt int // 중복 디비 접근 횟수 + duplicatedSnapshotReadCnt int // 중복 스냅샷 접근 횟수 + readDbKeySet *map[string]bool + readDbKeySetLock sync.RWMutex + readSnapshotDbKeySet *map[string]bool + readSnapshotDbKeySetLock sync.RWMutex + rpcLock sync.Mutex +} + +var Gr = Record{ + ancientDbReadCnt: 0, + levelDbReadCnt: 0, + levelDbSnapshotReadCnt: 0, + duplicatedReadCnt: 0, + duplicatedSnapshotReadCnt: 0, + readDbKeySet: &map[string]bool{}, + readDbKeySetLock: sync.RWMutex{}, + readSnapshotDbKeySet: &map[string]bool{}, + readSnapshotDbKeySetLock: sync.RWMutex{}, + rpcLock: sync.Mutex{}, +} + +func (gr *Record) Lock() { + gr.rpcLock.Lock() +} + +func (gr *Record) Unlock() { + gr.rpcLock.Unlock() +} + +func (gr *Record) Reset() { + gr.ancientDbReadCnt = 0 + gr.levelDbReadCnt = 0 + gr.levelDbSnapshotReadCnt = 0 + gr.duplicatedReadCnt = 0 + gr.duplicatedSnapshotReadCnt = 0 + gr.readDbKeySet = &map[string]bool{} + gr.readSnapshotDbKeySet = &map[string]bool{} +} + +func (gr *Record) addReadKeySet(key string) { + gr.readDbKeySetLock.Lock() + defer gr.readDbKeySetLock.Unlock() + + (*gr.readDbKeySet)[key] = true +} + +func (gr *Record) addReadSnapshotKeySet(key string) { + gr.readSnapshotDbKeySetLock.Lock() + defer gr.readSnapshotDbKeySetLock.Unlock() + + (*gr.readSnapshotDbKeySet)[key] = true +} + +func (gr *Record) isAlreadyRead(key string) bool { + gr.readDbKeySetLock.RLock() + defer gr.readDbKeySetLock.RUnlock() + if (*gr.readDbKeySet)[key] == true { + return true + } + return false +} + +func (gr *Record) countDuplicatedKey(key []byte) { + keyStr := string(key[:]) + if gr.isAlreadyRead(keyStr) { + gr.duplicatedReadCnt++ + } else { + gr.addReadKeySet(keyStr) + } +} + +func (gr *Record) isAlreadyReadSnapshot(key string) bool { + gr.readSnapshotDbKeySetLock.RLock() + defer gr.readSnapshotDbKeySetLock.RUnlock() + if (*gr.readSnapshotDbKeySet)[key] == true { + return true + } + return false +} + +func (gr *Record) countDuplicatedSnapshotKey(key []byte) { + keyStr := string(key[:]) + if gr.isAlreadyReadSnapshot(keyStr) { + gr.duplicatedSnapshotReadCnt++ + } else { + gr.addReadSnapshotKeySet(keyStr) + } +} + +func (gr *Record) CountAncientDbRead(key []byte) { + gr.countDuplicatedKey(key) + gr.ancientDbReadCnt++ +} + +func (gr *Record) CountLevelDbRead(key []byte) { + gr.countDuplicatedKey(key) + gr.levelDbReadCnt++ +} + +func (gr *Record) CountLevelDbSnapshotRead(key []byte) { + gr.countDuplicatedSnapshotKey(key) + gr.levelDbSnapshotReadCnt++ +} + +func (gr *Record) AncientDbReadCnt() int { + return gr.ancientDbReadCnt +} + +func (gr *Record) LevelDbReadCnt() int { + return gr.levelDbReadCnt +} + +func (gr *Record) LevelDbSnapshotReadCnt() int { + return gr.levelDbSnapshotReadCnt +} + +func (gr *Record) DuplicatedReadCnt() int { + return gr.duplicatedReadCnt +} + +func (gr *Record) DuplicatedSnapshotReadCnt() int { + return gr.duplicatedSnapshotReadCnt +} diff --git a/rpc/handler.go b/rpc/handler.go index f44e4d7b01..b1ec725f97 100644 --- a/rpc/handler.go +++ b/rpc/handler.go @@ -19,6 +19,7 @@ package rpc import ( "context" "encoding/json" + "github.com/ethereum/go-ethereum/hook" "reflect" "strconv" "strings" @@ -509,8 +510,15 @@ func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage return msg.errorResponse(&invalidParamsError{err.Error()}) } start := time.Now() - answer := h.runMethod(cp.ctx, msg, callb, args) + hook.Gr.Lock() + hook.Gr.Reset() + h.log.Error("Before runMethod", "msg.Method", msg.Method, "args", args) + answer := h.runMethod(cp.ctx, msg, callb, args) + h.log.Error("Result", "levelDb", hook.Gr.LevelDbReadCnt(), "levelDb duplicated", hook.Gr.DuplicatedReadCnt(), "levelDbSnapshot", hook.Gr.LevelDbSnapshotReadCnt(), "levelDbSnapshot duplicated", hook.Gr.DuplicatedSnapshotReadCnt(), "ancientDb", hook.Gr.AncientDbReadCnt()) + h.log.Error("Statistics", "levelDbNewRead", hook.Gr.LevelDbReadCnt()-hook.Gr.DuplicatedReadCnt(), "levelDbSnapshotNewRead", hook.Gr.LevelDbSnapshotReadCnt()-hook.Gr.DuplicatedSnapshotReadCnt(), "ancientDbRead", hook.Gr.AncientDbReadCnt()) + h.log.Error("After runMethod", "msg.Method", msg.Method, "args", args) + hook.Gr.Unlock() // Collect the statistics for RPC calls if metrics is enabled. // We only care about pure rpc call. Filter out subscription. if callb != h.unsubscribeCb {