Go-ethereum 原始碼解析之 go-ethereum/ethdb/memory_database.go

weixin_33751566發表於2018-10-24

Go-ethereum 原始碼解析之 go-ethereum/ethdb/memory_database.go


Source code

// Copyright 2017 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/>.

package ethdb

import (
    "errors"
    "sync"

    "github.com/ethereum/go-ethereum/common"
)

/*
 * This is a test memory database. Do not use for any production it does not get persisted
 */
type MemDatabase struct {
    db   map[string][]byte
    lock sync.RWMutex
}

func NewMemDatabase() *MemDatabase {
    return &MemDatabase{
        db: make(map[string][]byte),
    }
}

func NewMemDatabaseWithCap(size int) *MemDatabase {
    return &MemDatabase{
        db: make(map[string][]byte, size),
    }
}

func (db *MemDatabase) Put(key []byte, value []byte) error {
    db.lock.Lock()
    defer db.lock.Unlock()

    db.db[string(key)] = common.CopyBytes(value)
    return nil
}

func (db *MemDatabase) Has(key []byte) (bool, error) {
    db.lock.RLock()
    defer db.lock.RUnlock()

    _, ok := db.db[string(key)]
    return ok, nil
}

func (db *MemDatabase) Get(key []byte) ([]byte, error) {
    db.lock.RLock()
    defer db.lock.RUnlock()

    if entry, ok := db.db[string(key)]; ok {
        return common.CopyBytes(entry), nil
    }
    return nil, errors.New("not found")
}

func (db *MemDatabase) Keys() [][]byte {
    db.lock.RLock()
    defer db.lock.RUnlock()

    keys := [][]byte{}
    for key := range db.db {
        keys = append(keys, []byte(key))
    }
    return keys
}

func (db *MemDatabase) Delete(key []byte) error {
    db.lock.Lock()
    defer db.lock.Unlock()

    delete(db.db, string(key))
    return nil
}

func (db *MemDatabase) Close() {}

func (db *MemDatabase) NewBatch() Batch {
    return &memBatch{db: db}
}

func (db *MemDatabase) Len() int { return len(db.db) }

type kv struct {
    k, v []byte
    del  bool
}

type memBatch struct {
    db     *MemDatabase
    writes []kv
    size   int
}

func (b *memBatch) Put(key, value []byte) error {
    b.writes = append(b.writes, kv{common.CopyBytes(key), common.CopyBytes(value), false})
    b.size += len(value)
    return nil
}

func (b *memBatch) Delete(key []byte) error {
    b.writes = append(b.writes, kv{common.CopyBytes(key), nil, true})
    b.size += 1
    return nil
}

func (b *memBatch) Write() error {
    b.db.lock.Lock()
    defer b.db.lock.Unlock()

    for _, kv := range b.writes {
        if kv.del {
            delete(b.db.db, string(kv.k))
            continue
        }
        b.db.db[string(kv.k)] = kv.v
    }
    return nil
}

func (b *memBatch) ValueSize() int {
    return b.size
}

func (b *memBatch) Reset() {
    b.writes = b.writes[:0]
    b.size = 0
}


Appendix A. 總體批註

實現了一個記憶體資料庫 MemDatabase 用於測試環境,但不能將其用於生產環境。

ethdb.MemDatabase 實現了介面 ethdb.Database。

ethdb.memBatch 在 ethdb.MemDatabase 的基礎上提供了批處理能力。

這裡將基於介面程式設計的思想展現的淋漓盡致。


Appendix B. 詳細批註

1. type MemDatabase struct

資料結構 MemDatabase 是一個測試記憶體資料庫。不要將其用於任何生產環境,因為它不會被持久化。

  • db map[string][]byte: key-value 對?
  • lock sync.RWMutex: 鎖

1.1 func NewMemDatabase() *MemDatabase

建構函式 NewMemDatabase() 建立物件 MemDatabase,並使用預設值初始化。

1.2 func NewMemDatabaseWithCap(size int) *MemDatabase

建構函式 NewMemDatabaseWithCap() 建立物件 MemDatabase,並設定 db 的大小。

1.3 func (db *MemDatabase) Put(key []byte, value []byte) error

方法 Put() 實現了介面 ethdb.Putter 和介面 ethdb.Database。

引數:

  • key []byte: key
  • value []byte: value

返回值:

  • 出錯返回錯誤訊息 error,否則返回 nil

主要實現:

  • 加鎖。程式碼為: db.lock.Lock()
  • defer 解鎖。程式碼為:defer db.lock.Unlock()
  • 將 (key, value) 對儲存資料庫 db。db.db[string(key)] = common.CopyBytes(value)

1.4 func (db *MemDatabase) Has(key []byte) (bool, error)

方法 Has() 實現了介面 ethdb.Database。

引數:

  • key []byte: key

返回值:

  • 存在返回 true,否則返回 false
  • 出錯返回錯誤訊息 error,否則返回 nil

主要實現:

  • 加鎖。程式碼為: db.lock.RLock()
  • defer 解鎖。程式碼為:defer db.lock.RUnlock()
  • 是否存在。_, ok := db.db[string(key)]

1.5 func (db *MemDatabase) Get(key []byte) ([]byte, error)

方法 Get() 實現了介面 ethdb.Database。

引數:

  • key []byte: key

返回值:

  • 存在返回 key 對應的 value
  • 出錯返回錯誤訊息 error,否則返回 nil

主要實現:

  • 加鎖。程式碼為:db.lock.RLock()
  • defer 解鎖。程式碼為:defer db.lock.RUnlock()
  • 獲取 key 對應的值 entry。程式碼為:entry, ok := db.db[string(key)]
  • 將 entry 的副本返回。程式碼為:return common.CopyBytes(entry)

1.6 func (db *MemDatabase) Keys() [][]byte

方法 Keys() 返回資料庫中的所有 key。

返回值:

  • 所有的 key 構成的列表

主要實現:

  • 加鎖。程式碼為:db.lock.RLock()
  • defer 解鎖。程式碼為:defer db.lock.RUnlock()
  • 定義所有 key 的列表 keys
  • 遍歷資料庫 db.db 中的所有 key
    • 將 key 新增到 keys

1.7 func (db *MemDatabase) Delete(key []byte) error

方法 Put() 實現了介面 ethdb.Deleter 和介面 ethdb.Database。

引數:

  • key []byte: key

返回值:

  • 出錯返回錯誤訊息 error,否則返回 nil

主要實現:

  • 加鎖。程式碼為:db.lock.Lock()
  • defer 解鎖。程式碼為:defer db.lock.Unlock()
  • 通過 Go 內建函式 delete() 從資料庫 db.db 中刪除對應的 key。程式碼為:delete(db.db, string(key))

1.8 func (db *MemDatabase) Close() {}

方法 Close() 實現了介面 ethdb.Database。

主要實現:

  • 空實現。

1.9 func (db *MemDatabase) NewBatch() Batch

方法 NewBatch() 實現了介面 ethdb.Database。

主要實現:

  • return &memBatch{db: db}

1.10 func (db *MemDatabase) Len() int

方法 Len() 返回資料庫包含的資料量。

返回值:

  • 資料量

主要實現:

  • return len(db.db)

2. type kv struct

資料結構 kv 用於描述批處理的值 k, v 和操作型別是 add 還是 del。

  • k, v []byte: Key & Value
  • del bool: 操作型別是插入還是刪除

3. type memBatch struct

資料結構 memBatch 是具有批處理能力的記憶體資料庫。

  • db *MemDatabase: 記憶體資料庫
  • writes []kv: 批處理資料
  • size int: 批處理的位元組數

3.1 func (b *memBatch) Put(key, value []byte) error

方法 Put() 實現了介面 ethdb.Putter,用於將給定的 key & value 插入資料庫。

引數:

  • key []byte: key
  • value []byte: value

返回值:

  • 出錯返回錯誤訊息 error,否則返回 nil

主要實現:

  • 將 key & value & false 構建的 kv 插入批處理資料 writes
    • b.writes = append(b.writes, kv{common.CopyBytes(key), common.CopyBytes(value), false})
  • 增加批處理位元組數 size
    • b.size += len(value)

3.2 func (b *memBatch) Delete(key []byte) error

方法 Delete() 實現了介面 ethdb.Deleter,用於從資料庫中刪除給定的 key。

引數:

  • key []byte: key

返回值:

  • 出錯返回錯誤訊息 error,否則返回 nil

主要實現:

  • 將 key & nil & true 構建的 kv 插入批處理資料 writes
    • b.writes = append(b.writes, kv{common.CopyBytes(key), nil, true})
  • 更新批處理位元組數 size
    • b.size += 1

3.3 func (b *memBatch) Write() error

方法 Write() 一次性將批處理資料更新到資料庫。

返回值:

  • 出錯返回錯誤訊息 error,否則返回 nil

主要實現:

  • 加鎖。程式碼為:db.lock.Lock()
  • defer 解鎖。程式碼為:defer db.lock.Unlock()
  • 遍歷批處理資料 b.writes 的每個 kv
    • 如果 kv.del
      • 從資料庫中刪除 kv.k
        • delete(b.db.db, string(kv.k))
      • 退出本輪迭代
    • 否則,將 kv.k & kv.v 插入資料庫
      • b.db.db[string(kv.k)] = kv.v

3.4 func (b *memBatch) ValueSize() int

方法 ValueSize() 返回批處理位元組數。

返回值:

  • 批處理位元組數。

主要實現:

  • return b.size

3.5 func (b *memBatch) Reset()

方法 Reset() 重置批處理操作。

主要實現:

  • 清空批處理操作
    • b.writes = b.writes[:0]
    • b.size = 0

Reference

  1. https://github.com/ethereum/go-ethereum/blob/master/ethdb/memory_database.go

Contributor

  1. Windstamp, https://github.com/windstamp

相關文章