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

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. 總體批注

實(shí)現(xiàn)了一個(gè)內(nèi)存數(shù)據(jù)庫(kù) MemDatabase 用于測(cè)試環(huán)境,但不能將其用于生產(chǎn)環(huán)境谨设。

ethdb.MemDatabase 實(shí)現(xiàn)了接口 ethdb.Database婉商。

ethdb.memBatch 在 ethdb.MemDatabase 的基礎(chǔ)上提供了批處理能力弯予。

這里將基于接口編程的思想展現(xiàn)的淋漓盡致。


Appendix B. 詳細(xì)批注

1. type MemDatabase struct

數(shù)據(jù)結(jié)構(gòu) MemDatabase 是一個(gè)測(cè)試內(nèi)存數(shù)據(jù)庫(kù)告组。不要將其用于任何生產(chǎn)環(huán)境吆玖,因?yàn)樗粫?huì)被持久化。

  • db map[string][]byte: key-value 對(duì)杨刨?
  • lock sync.RWMutex: 鎖

1.1 func NewMemDatabase() *MemDatabase

構(gòu)造函數(shù) NewMemDatabase() 創(chuàng)建對(duì)象 MemDatabase,并使用默認(rèn)值初始化擦剑。

1.2 func NewMemDatabaseWithCap(size int) *MemDatabase

構(gòu)造函數(shù) NewMemDatabaseWithCap() 創(chuàng)建對(duì)象 MemDatabase妖胀,并設(shè)定 db 的大小。

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

方法 Put() 實(shí)現(xiàn)了接口 ethdb.Putter 和接口 ethdb.Database惠勒。

參數(shù):

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

返回值:

  • 出錯(cuò)返回錯(cuò)誤消息 error赚抡,否則返回 nil

主要實(shí)現(xiàn):

  • 加鎖。代碼為: db.lock.Lock()
  • defer 解鎖纠屋。代碼為:defer db.lock.Unlock()
  • 將 (key, value) 對(duì)存儲(chǔ)數(shù)據(jù)庫(kù) db涂臣。db.db[string(key)] = common.CopyBytes(value)

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

方法 Has() 實(shí)現(xiàn)了接口 ethdb.Database。

參數(shù):

  • key []byte: key

返回值:

  • 存在返回 true售担,否則返回 false
  • 出錯(cuò)返回錯(cuò)誤消息 error赁遗,否則返回 nil

主要實(shí)現(xiàn):

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

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

方法 Get() 實(shí)現(xiàn)了接口 ethdb.Database。

參數(shù):

  • key []byte: key

返回值:

  • 存在返回 key 對(duì)應(yīng)的 value
  • 出錯(cuò)返回錯(cuò)誤消息 error骑素,否則返回 nil

主要實(shí)現(xiàn):

  • 加鎖炫乓。代碼為:db.lock.RLock()
  • defer 解鎖。代碼為:defer db.lock.RUnlock()
  • 獲取 key 對(duì)應(yīng)的值 entry献丑。代碼為:entry, ok := db.db[string(key)]
  • 將 entry 的副本返回末捣。代碼為:return common.CopyBytes(entry)

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

方法 Keys() 返回?cái)?shù)據(jù)庫(kù)中的所有 key。

返回值:

  • 所有的 key 構(gòu)成的列表

主要實(shí)現(xiàn):

  • 加鎖创橄。代碼為:db.lock.RLock()
  • defer 解鎖箩做。代碼為:defer db.lock.RUnlock()
  • 定義所有 key 的列表 keys
  • 遍歷數(shù)據(jù)庫(kù) db.db 中的所有 key
    • 將 key 添加到 keys

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

方法 Put() 實(shí)現(xiàn)了接口 ethdb.Deleter 和接口 ethdb.Database。

參數(shù):

  • key []byte: key

返回值:

  • 出錯(cuò)返回錯(cuò)誤消息 error妥畏,否則返回 nil

主要實(shí)現(xiàn):

  • 加鎖邦邦。代碼為:db.lock.Lock()
  • defer 解鎖。代碼為:defer db.lock.Unlock()
  • 通過(guò) Go 內(nèi)置函數(shù) delete() 從數(shù)據(jù)庫(kù) db.db 中刪除對(duì)應(yīng)的 key醉蚁。代碼為:delete(db.db, string(key))

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

方法 Close() 實(shí)現(xiàn)了接口 ethdb.Database燃辖。

主要實(shí)現(xiàn):

  • 空實(shí)現(xiàn)。

1.9 func (db *MemDatabase) NewBatch() Batch

方法 NewBatch() 實(shí)現(xiàn)了接口 ethdb.Database网棍。

主要實(shí)現(xiàn):

  • return &memBatch{db: db}

1.10 func (db *MemDatabase) Len() int

方法 Len() 返回?cái)?shù)據(jù)庫(kù)包含的數(shù)據(jù)量黔龟。

返回值:

  • 數(shù)據(jù)量

主要實(shí)現(xiàn):

  • return len(db.db)

2. type kv struct

數(shù)據(jù)結(jié)構(gòu) kv 用于描述批處理的值 k, v 和操作類型是 add 還是 del。

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

3. type memBatch struct

數(shù)據(jù)結(jié)構(gòu) memBatch 是具有批處理能力的內(nèi)存數(shù)據(jù)庫(kù)滥玷。

  • db *MemDatabase: 內(nèi)存數(shù)據(jù)庫(kù)
  • writes []kv: 批處理數(shù)據(jù)
  • size int: 批處理的字節(jié)數(shù)

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

方法 Put() 實(shí)現(xiàn)了接口 ethdb.Putter氏身,用于將給定的 key & value 插入數(shù)據(jù)庫(kù)。

參數(shù):

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

返回值:

  • 出錯(cuò)返回錯(cuò)誤消息 error惑畴,否則返回 nil

主要實(shí)現(xiàn):

  • 將 key & value & false 構(gòu)建的 kv 插入批處理數(shù)據(jù) writes
    • b.writes = append(b.writes, kv{common.CopyBytes(key), common.CopyBytes(value), false})
  • 增加批處理字節(jié)數(shù) size
    • b.size += len(value)

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

方法 Delete() 實(shí)現(xiàn)了接口 ethdb.Deleter蛋欣,用于從數(shù)據(jù)庫(kù)中刪除給定的 key。

參數(shù):

  • key []byte: key

返回值:

  • 出錯(cuò)返回錯(cuò)誤消息 error如贷,否則返回 nil

主要實(shí)現(xiàn):

  • 將 key & nil & true 構(gòu)建的 kv 插入批處理數(shù)據(jù) writes
    • b.writes = append(b.writes, kv{common.CopyBytes(key), nil, true})
  • 更新批處理字節(jié)數(shù) size
    • b.size += 1

3.3 func (b *memBatch) Write() error

方法 Write() 一次性將批處理數(shù)據(jù)更新到數(shù)據(jù)庫(kù)陷虎。

返回值:

  • 出錯(cuò)返回錯(cuò)誤消息 error,否則返回 nil

主要實(shí)現(xiàn):

  • 加鎖杠袱。代碼為:db.lock.Lock()
  • defer 解鎖泻红。代碼為:defer db.lock.Unlock()
  • 遍歷批處理數(shù)據(jù) b.writes 的每個(gè) kv
    • 如果 kv.del
      • 從數(shù)據(jù)庫(kù)中刪除 kv.k
        • delete(b.db.db, string(kv.k))
      • 退出本輪迭代
    • 否則,將 kv.k & kv.v 插入數(shù)據(jù)庫(kù)
      • b.db.db[string(kv.k)] = kv.v

3.4 func (b *memBatch) ValueSize() int

方法 ValueSize() 返回批處理字節(jié)數(shù)霞掺。

返回值:

  • 批處理字節(jié)數(shù)谊路。

主要實(shí)現(xiàn):

  • return b.size

3.5 func (b *memBatch) Reset()

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

主要實(shí)現(xiàn):

  • 清空批處理操作
    • 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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末菩彬,一起剝皮案震驚了整個(gè)濱河市缠劝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌骗灶,老刑警劉巖惨恭,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異耙旦,居然都是意外死亡脱羡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)锉罐,“玉大人帆竹,你說(shuō)我怎么就攤上這事∨Ч妫” “怎么了栽连?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)侨舆。 經(jīng)常有香客問(wèn)我秒紧,道長(zhǎng),這世上最難降的妖魔是什么挨下? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任熔恢,我火速辦了婚禮,結(jié)果婚禮上臭笆,老公的妹妹穿的比我還像新娘绩聘。我一直安慰自己,他們只是感情好耗啦,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布凿菩。 她就那樣靜靜地躺著,像睡著了一般帜讲。 火紅的嫁衣襯著肌膚如雪衅谷。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天似将,我揣著相機(jī)與錄音获黔,去河邊找鬼。 笑死在验,一個(gè)胖子當(dāng)著我的面吹牛玷氏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播腋舌,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼盏触,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了块饺?” 一聲冷哼從身側(cè)響起赞辩,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎授艰,沒(méi)想到半個(gè)月后辨嗽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淮腾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年糟需,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了屉佳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡洲押,死狀恐怖武花,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情诅诱,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布送朱,位于F島的核電站娘荡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏驶沼。R本人自食惡果不足惜炮沐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望回怜。 院中可真熱鬧大年,春花似錦、人聲如沸玉雾。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)复旬。三九已至垦缅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驹碍,已是汗流浹背壁涎。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留志秃,地道東北人怔球。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像浮还,于是被迫代替她去往敵國(guó)和親竟坛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容