mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 03:30:37 +00:00
Use KV Abstraction in RestAPI (#400)
* Introduce NoValuesCursor. From() method is useless because can be replaced by Seek().` * implement NoValueCursor interface * use abstract db in restapi * cleanup .md
This commit is contained in:
parent
1fb8749638
commit
b490192e67
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/ledgerwatch/turbo-geth/common"
|
"github.com/ledgerwatch/turbo-geth/common"
|
||||||
"github.com/ledgerwatch/turbo-geth/common/dbutils"
|
"github.com/ledgerwatch/turbo-geth/common/dbutils"
|
||||||
"github.com/ledgerwatch/turbo-geth/core/types/accounts"
|
"github.com/ledgerwatch/turbo-geth/core/types/accounts"
|
||||||
"github.com/ledgerwatch/turbo-geth/ethdb/remote"
|
"github.com/ledgerwatch/turbo-geth/ethdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterAccountAPI(router *gin.RouterGroup, e *Env) error {
|
func RegisterAccountAPI(router *gin.RouterGroup, e *Env) error {
|
||||||
@ -42,13 +42,13 @@ func jsonifyAccount(account *accounts.Account) map[string]interface{} {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func findAccountByID(accountID string, remoteDB *remote.DB) (*accounts.Account, error) {
|
func findAccountByID(accountID string, remoteDB ethdb.KV) (*accounts.Account, error) {
|
||||||
|
|
||||||
possibleKeys := getPossibleKeys(accountID)
|
possibleKeys := getPossibleKeys(accountID)
|
||||||
|
|
||||||
var account *accounts.Account
|
var account *accounts.Account
|
||||||
|
|
||||||
err := remoteDB.View(context.TODO(), func(tx *remote.Tx) error {
|
err := remoteDB.View(context.TODO(), func(tx ethdb.Tx) error {
|
||||||
bucket := tx.Bucket(dbutils.AccountsBucket)
|
bucket := tx.Bucket(dbutils.AccountsBucket)
|
||||||
|
|
||||||
for _, key := range possibleKeys {
|
for _, key := range possibleKeys {
|
||||||
|
@ -3,11 +3,11 @@ package apis
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/ledgerwatch/turbo-geth/ethdb/remote"
|
"github.com/ledgerwatch/turbo-geth/ethdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrEntityNotFound = errors.New("entity not found")
|
var ErrEntityNotFound = errors.New("entity not found")
|
||||||
|
|
||||||
type Env struct {
|
type Env struct {
|
||||||
DB *remote.DB
|
DB ethdb.KV
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/ledgerwatch/turbo-geth/ethdb/remote"
|
"github.com/ledgerwatch/turbo-geth/ethdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterRemoteDBAPI(router *gin.RouterGroup, e *Env) error {
|
func RegisterRemoteDBAPI(router *gin.RouterGroup, e *Env) error {
|
||||||
@ -17,7 +17,8 @@ func RegisterRemoteDBAPI(router *gin.RouterGroup, e *Env) error {
|
|||||||
|
|
||||||
func (e *Env) GetDB(c *gin.Context) {
|
func (e *Env) GetDB(c *gin.Context) {
|
||||||
var host, port string
|
var host, port string
|
||||||
split := strings.Split(e.DB.GetDialAddr(), ":")
|
|
||||||
|
split := strings.Split(e.DB.Options().Remote.DialAddress, ":")
|
||||||
if len(split) == 2 {
|
if len(split) == 2 {
|
||||||
host, port = split[0], split[1]
|
host, port = split[0], split[1]
|
||||||
}
|
}
|
||||||
@ -26,7 +27,7 @@ func (e *Env) GetDB(c *gin.Context) {
|
|||||||
|
|
||||||
func (e *Env) PostDB(c *gin.Context) {
|
func (e *Env) PostDB(c *gin.Context) {
|
||||||
newAddr := c.Query("host") + ":" + c.Query("port")
|
newAddr := c.Query("host") + ":" + c.Query("port")
|
||||||
remoteDB, err := remote.Open(context.Background(), remote.DefaultOpts.Addr(newAddr))
|
remoteDB, err := ethdb.NewRemote().Path(newAddr).Open(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Error(err) //nolint:errcheck
|
c.Error(err) //nolint:errcheck
|
||||||
return
|
return
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package apis
|
package apis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -9,7 +8,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/ledgerwatch/turbo-geth/common"
|
"github.com/ledgerwatch/turbo-geth/common"
|
||||||
"github.com/ledgerwatch/turbo-geth/common/dbutils"
|
"github.com/ledgerwatch/turbo-geth/common/dbutils"
|
||||||
"github.com/ledgerwatch/turbo-geth/ethdb/remote"
|
"github.com/ledgerwatch/turbo-geth/ethdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterStorageAPI(router *gin.RouterGroup, e *Env) error {
|
func RegisterStorageAPI(router *gin.RouterGroup, e *Env) error {
|
||||||
@ -32,19 +31,16 @@ type StorageResponse struct {
|
|||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func findStorageByPrefix(prefixS string, remoteDB *remote.DB) ([]*StorageResponse, error) {
|
func findStorageByPrefix(prefixS string, remoteDB ethdb.KV) ([]*StorageResponse, error) {
|
||||||
var results []*StorageResponse
|
var results []*StorageResponse
|
||||||
prefix := common.FromHex(prefixS)
|
prefix := common.FromHex(prefixS)
|
||||||
if err := remoteDB.View(context.TODO(), func(tx *remote.Tx) error {
|
if err := remoteDB.View(context.TODO(), func(tx ethdb.Tx) error {
|
||||||
c := tx.Bucket(dbutils.StorageBucket).Cursor(remote.DefaultCursorOpts)
|
c := tx.Bucket(dbutils.StorageBucket).Cursor().Prefix(prefix).Prefetch(200)
|
||||||
|
|
||||||
for k, v, err := c.Seek(prefix); k != nil; k, v, err = c.Next() {
|
for k, v, err := c.First(); k != nil || err != nil; k, v, err = c.Next() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !bytes.HasPrefix(k, prefix) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
results = append(results, &StorageResponse{
|
results = append(results, &StorageResponse{
|
||||||
Prefix: fmt.Sprintf("%x\n", k),
|
Prefix: fmt.Sprintf("%x\n", k),
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/ledgerwatch/turbo-geth/common"
|
"github.com/ledgerwatch/turbo-geth/common"
|
||||||
"github.com/ledgerwatch/turbo-geth/common/dbutils"
|
"github.com/ledgerwatch/turbo-geth/common/dbutils"
|
||||||
"github.com/ledgerwatch/turbo-geth/ethdb/remote"
|
"github.com/ledgerwatch/turbo-geth/ethdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterStorageTombstonesAPI(router *gin.RouterGroup, e *Env) error {
|
func RegisterStorageTombstonesAPI(router *gin.RouterGroup, e *Env) error {
|
||||||
@ -39,23 +39,20 @@ type StorageTombsResponse struct {
|
|||||||
HideStorage bool `json:"hideStorage"`
|
HideStorage bool `json:"hideStorage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func findStorageTombstoneByPrefix(prefixS string, remoteDB *remote.DB) ([]*StorageTombsResponse, error) {
|
func findStorageTombstoneByPrefix(prefixS string, remoteDB ethdb.KV) ([]*StorageTombsResponse, error) {
|
||||||
var results []*StorageTombsResponse
|
var results []*StorageTombsResponse
|
||||||
prefix := common.FromHex(prefixS)
|
prefix := common.FromHex(prefixS)
|
||||||
if err := remoteDB.View(context.TODO(), func(tx *remote.Tx) error {
|
if err := remoteDB.View(context.TODO(), func(tx ethdb.Tx) error {
|
||||||
interBucket := tx.Bucket(dbutils.IntermediateTrieHashBucket)
|
interBucket := tx.Bucket(dbutils.IntermediateTrieHashBucket)
|
||||||
c := interBucket.Cursor(remote.DefaultCursorOpts.PrefetchValues(true))
|
c := interBucket.Cursor().Prefix(prefix).NoValues()
|
||||||
storage := tx.Bucket(dbutils.StorageBucket).Cursor(remote.DefaultCursorOpts.PrefetchValues(false).PrefetchSize(1))
|
storage := tx.Bucket(dbutils.StorageBucket).Cursor().Prefetch(1).NoValues()
|
||||||
|
|
||||||
for k, v, err := c.Seek(prefix); k != nil; k, v, err = c.Next() {
|
for k, vSize, err := c.First(); k != nil || err != nil; k, vSize, err = c.Next() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !bytes.HasPrefix(k, prefix) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v) > 0 {
|
if vSize > 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,9 +110,9 @@ type IntegrityCheck struct {
|
|||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func storageTombstonesIntegrityDBCheck(remoteDB *remote.DB) ([]*IntegrityCheck, error) {
|
func storageTombstonesIntegrityDBCheck(remoteDB ethdb.KV) ([]*IntegrityCheck, error) {
|
||||||
var results []*IntegrityCheck
|
var results []*IntegrityCheck
|
||||||
return results, remoteDB.View(context.TODO(), func(tx *remote.Tx) error {
|
return results, remoteDB.View(context.TODO(), func(tx ethdb.Tx) error {
|
||||||
res, err := storageTombstonesIntegrityDBCheckTx(tx)
|
res, err := storageTombstonesIntegrityDBCheckTx(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -125,50 +122,25 @@ func storageTombstonesIntegrityDBCheck(remoteDB *remote.DB) ([]*IntegrityCheck,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func storageTombstonesIntegrityDBCheckTx(tx *remote.Tx) ([]*IntegrityCheck, error) {
|
func storageTombstonesIntegrityDBCheckTx(tx ethdb.Tx) ([]*IntegrityCheck, error) {
|
||||||
var res []*IntegrityCheck
|
var res []*IntegrityCheck
|
||||||
var check1 = &IntegrityCheck{
|
check1 := &IntegrityCheck{
|
||||||
Name: "1 trie prefix must be covered only by 1 tombstone",
|
|
||||||
Value: "ok",
|
|
||||||
}
|
|
||||||
res = append(res, check1)
|
|
||||||
check2 := &IntegrityCheck{
|
|
||||||
Name: "tombstone must hide at least 1 storage",
|
Name: "tombstone must hide at least 1 storage",
|
||||||
Value: "ok",
|
Value: "ok",
|
||||||
}
|
}
|
||||||
res = append(res, check2)
|
res = append(res, check1)
|
||||||
|
|
||||||
inter := tx.Bucket(dbutils.IntermediateTrieHashBucket).Cursor(remote.DefaultCursorOpts.PrefetchValues(true).PrefetchSize(1000))
|
inter := tx.Bucket(dbutils.IntermediateTrieHashBucket).Cursor().Prefetch(1000).NoValues()
|
||||||
cOverlap := tx.Bucket(dbutils.IntermediateTrieHashBucket).Cursor(remote.DefaultCursorOpts.PrefetchValues(true).PrefetchSize(10))
|
storage := tx.Bucket(dbutils.StorageBucket).Cursor().Prefetch(10).NoValues()
|
||||||
storage := tx.Bucket(dbutils.StorageBucket).Cursor(remote.DefaultCursorOpts.PrefetchValues(false).PrefetchSize(10))
|
|
||||||
|
|
||||||
for k, v, err := inter.First(); k != nil; k, v, err = inter.Next() {
|
for k, vSize, err := inter.First(); k != nil || err != nil; k, vSize, err = inter.Next() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(v) > 0 {
|
if vSize > 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1 prefix must be covered only by 1 tombstone
|
|
||||||
from := append(k, []byte{0, 0}...)
|
|
||||||
for overlapK, overlapV, err := cOverlap.Seek(from); overlapK != nil; overlapK, overlapV, err = cOverlap.Next() {
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !bytes.HasPrefix(overlapK, from) {
|
|
||||||
overlapK = nil
|
|
||||||
}
|
|
||||||
if len(overlapV) > 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if bytes.HasPrefix(overlapK, k) {
|
|
||||||
check1.Value = fmt.Sprintf("%x is prefix of %x\n", overlapK, k)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// each tombstone must hide at least 1 storage
|
// each tombstone must hide at least 1 storage
|
||||||
addrHash := common.CopyBytes(k[:common.HashLength])
|
addrHash := common.CopyBytes(k[:common.HashLength])
|
||||||
storageK, _, err := storage.Seek(addrHash)
|
storageK, _, err := storage.Seek(addrHash)
|
||||||
@ -193,7 +165,7 @@ func storageTombstonesIntegrityDBCheckTx(tx *remote.Tx) ([]*IntegrityCheck, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !hideStorage {
|
if !hideStorage {
|
||||||
check2.Value = fmt.Sprintf("tombstone %x has no storage to hide\n", k)
|
check1.Value = fmt.Sprintf("tombstone %x has no storage to hide\n", k)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/ledgerwatch/turbo-geth/cmd/restapi/apis"
|
"github.com/ledgerwatch/turbo-geth/cmd/restapi/apis"
|
||||||
"github.com/ledgerwatch/turbo-geth/ethdb/remote"
|
"github.com/ledgerwatch/turbo-geth/ethdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func printError(name string, err error) {
|
func printError(name string, err error) {
|
||||||
@ -29,7 +29,7 @@ func ServeREST(localAddress, remoteDbAddress string) error {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
db, err := remote.Open(context.Background(), remote.DefaultOpts.Addr(remoteDbAddress))
|
db, err := ethdb.NewRemote().Path(remoteDbAddress).Open(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -271,7 +271,7 @@ func ClearTombstonesForReCreatedAccount(db ethdb.MinDatabase, addrHash common.Ha
|
|||||||
}
|
}
|
||||||
|
|
||||||
var boltDb *bolt.DB
|
var boltDb *bolt.DB
|
||||||
if hasBolt, ok := db.(ethdb.KV); ok {
|
if hasBolt, ok := db.(ethdb.HasKV); ok {
|
||||||
boltDb = hasBolt.KV()
|
boltDb = hasBolt.KV()
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("only Bolt supported yet, given: %T", db)
|
return fmt.Errorf("only Bolt supported yet, given: %T", db)
|
||||||
@ -333,7 +333,7 @@ func PutTombstoneForDeletedAccount(db ethdb.MinDatabase, addrHash []byte) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
var boltDb *bolt.DB
|
var boltDb *bolt.DB
|
||||||
if hasKV, ok := db.(ethdb.KV); ok {
|
if hasKV, ok := db.(ethdb.HasKV); ok {
|
||||||
boltDb = hasKV.KV()
|
boltDb = hasKV.KV()
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("only Bolt supported yet, given: %T", db)
|
return fmt.Errorf("only Bolt supported yet, given: %T", db)
|
||||||
@ -377,7 +377,7 @@ func PutTombstoneForDeletedAccount(db ethdb.MinDatabase, addrHash []byte) error
|
|||||||
|
|
||||||
func ClearTombstonesForNewStorage(db ethdb.MinDatabase, storageKeyNoInc []byte) error {
|
func ClearTombstonesForNewStorage(db ethdb.MinDatabase, storageKeyNoInc []byte) error {
|
||||||
var boltDb *bolt.DB
|
var boltDb *bolt.DB
|
||||||
if hasKV, ok := db.(ethdb.KV); ok {
|
if hasKV, ok := db.(ethdb.HasKV); ok {
|
||||||
boltDb = hasKV.KV()
|
boltDb = hasKV.KV()
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("only Bolt supported yet, given: %T", db)
|
return fmt.Errorf("only Bolt supported yet, given: %T", db)
|
||||||
|
@ -1215,7 +1215,7 @@ func TestClearTombstonesForReCreatedAccount(t *testing.T) {
|
|||||||
|
|
||||||
//printBucket := func() {
|
//printBucket := func() {
|
||||||
// fmt.Printf("IH bucket print\n")
|
// fmt.Printf("IH bucket print\n")
|
||||||
// _ = db.KV().View(func(tx *bolt.Tx) error {
|
// _ = db.HasKV().View(func(tx *bolt.Tx) error {
|
||||||
// tx.Bucket(dbutils.IntermediateTrieHashBucket).ForEach(func(k, v []byte) error {
|
// tx.Bucket(dbutils.IntermediateTrieHashBucket).ForEach(func(k, v []byte) error {
|
||||||
// if len(v) == 0 {
|
// if len(v) == 0 {
|
||||||
// fmt.Printf("IH: %x\n", k)
|
// fmt.Printf("IH: %x\n", k)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, {useState} from 'react';
|
||||||
|
|
||||||
import {Col, Container, Nav, Row} from 'react-bootstrap';
|
import {Col, Container, Nav, Row} from 'react-bootstrap';
|
||||||
import API from './utils/API.js'
|
import API from './utils/API.js'
|
||||||
@ -9,9 +9,8 @@ import StorageTombstonesPage from './page/StorageTombstonesPage';
|
|||||||
import {ReactComponent as Logo} from './logo.svg';
|
import {ReactComponent as Logo} from './logo.svg';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import StoragePage from './page/Storage';
|
import StoragePage from './page/Storage';
|
||||||
import RemoteDBForm from './components/RemoteDBForm';
|
import RemoteSidebar from './components/RemoteSidebar';
|
||||||
|
|
||||||
const api = new API('http://localhost:8080')
|
|
||||||
const sidebar = [
|
const sidebar = [
|
||||||
{
|
{
|
||||||
url: '/accounts',
|
url: '/accounts',
|
||||||
@ -28,6 +27,15 @@ const sidebar = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const [host, setHost] = useState('localhost');
|
||||||
|
const [port, setPort] = useState('8080');
|
||||||
|
const onApiChange = (data) => {
|
||||||
|
setHost(data.host)
|
||||||
|
setPort(data.port)
|
||||||
|
}
|
||||||
|
|
||||||
|
const api = new API('http://' + host + ':' + port)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorCatcher>
|
<ErrorCatcher>
|
||||||
<Router>
|
<Router>
|
||||||
@ -47,7 +55,8 @@ function App() {
|
|||||||
<div className="active-pointer"/>
|
<div className="active-pointer"/>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
)}
|
)}
|
||||||
<RemoteDBForm api={api}/>
|
<div className="mt-5 border-secondary border-top"/>
|
||||||
|
<RemoteSidebar api={api} restHost={host} restPort={port} onApiChange={onApiChange}/>
|
||||||
</Nav>
|
</Nav>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={9} md={10} lg={11}>
|
<Col xs={9} md={10} lg={11}>
|
||||||
|
@ -1,111 +0,0 @@
|
|||||||
import React, {useState} from 'react';
|
|
||||||
|
|
||||||
import Form from 'react-bootstrap/Form';
|
|
||||||
import Button from 'react-bootstrap/Button'
|
|
||||||
import {InputGroup} from 'react-bootstrap';
|
|
||||||
import Modal from 'react-bootstrap/Modal';
|
|
||||||
|
|
||||||
const get = (api, setState) => {
|
|
||||||
setState({host: '', port: '', loading: true});
|
|
||||||
const lookupSuccess = (response) => setState({host: response.data.host, port: response.data.port, loading: false});
|
|
||||||
const lookupFail = (error) => {
|
|
||||||
setState({host: '', port: '', loading: false})
|
|
||||||
|
|
||||||
setState(() => {
|
|
||||||
throw error
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return api.getRemoteDB().then(lookupSuccess).catch(lookupFail);
|
|
||||||
}
|
|
||||||
|
|
||||||
const set = (host, port, api, setState) => {
|
|
||||||
setState({host: host, port: port, loading: true});
|
|
||||||
|
|
||||||
const lookupSuccess = () => setState({host: host, port: port, loading: false});
|
|
||||||
const lookupFail = (error) => {
|
|
||||||
setState({host: host, port: port, loading: true});
|
|
||||||
|
|
||||||
setState(() => {
|
|
||||||
throw error
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return api.setRemoteDB(host, port).then(lookupSuccess).catch(lookupFail);
|
|
||||||
}
|
|
||||||
|
|
||||||
const RemoteDBForm = ({api}) => {
|
|
||||||
const [state, setState] = useState({host: '', port: ''});
|
|
||||||
const [show, setShow] = useState(false);
|
|
||||||
|
|
||||||
const handleHostChange = (event) => {
|
|
||||||
const host = event.target.value;
|
|
||||||
setState((prev) => {
|
|
||||||
return {host: host, port: prev.port};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const handlePortChange = (event) => {
|
|
||||||
const port = event.target.value;
|
|
||||||
setState((prev) => {
|
|
||||||
return {host: prev.host, port: port};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSubmit = (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
set(state.host, state.port, api, setState)
|
|
||||||
setShow(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClick = () => {
|
|
||||||
setShow(true)
|
|
||||||
get(api, setState)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mt-3">
|
|
||||||
<Button variant="outline-secondary" size="sm"
|
|
||||||
className="w-100 rounded-0 text-break"
|
|
||||||
onClick={handleClick}>
|
|
||||||
Remote DB<br/>
|
|
||||||
{state.host && state.host + ':' + state.port}
|
|
||||||
</Button>
|
|
||||||
<Modal show={show} onHide={() => setShow(false)}>
|
|
||||||
<Modal.Header>
|
|
||||||
<Modal.Title>Remote DB</Modal.Title>
|
|
||||||
</Modal.Header>
|
|
||||||
<Modal.Body>
|
|
||||||
<Form onSubmit={handleSubmit}>
|
|
||||||
<InputGroup className="mb-1" size="sm">
|
|
||||||
<Input label="Host" value={state.host} onChange={handleHostChange}/>
|
|
||||||
</InputGroup>
|
|
||||||
<InputGroup className="mb-1" size="sm">
|
|
||||||
<Input label="Port" value={state.port} onChange={handlePortChange}/>
|
|
||||||
<InputGroup.Append>
|
|
||||||
<Button variant="outline-primary" type="submit">Button</Button>
|
|
||||||
</InputGroup.Append>
|
|
||||||
</InputGroup>
|
|
||||||
|
|
||||||
</Form>
|
|
||||||
</Modal.Body>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Input = ({label, value, onChange,}) => (
|
|
||||||
<React.Fragment>
|
|
||||||
<InputGroup.Prepend>
|
|
||||||
<InputGroup.Text id="addon{label}">{label}</InputGroup.Text>
|
|
||||||
</InputGroup.Prepend>
|
|
||||||
<Form.Control
|
|
||||||
type="text"
|
|
||||||
placeholder="{Label}"
|
|
||||||
aria-describedby="addon{label}"
|
|
||||||
name="{label}"
|
|
||||||
value={value || ''}
|
|
||||||
onChange={onChange}/>
|
|
||||||
</React.Fragment>
|
|
||||||
)
|
|
||||||
|
|
||||||
export default RemoteDBForm;
|
|
132
debug-web-ui/src/components/RemoteSidebar.js
Normal file
132
debug-web-ui/src/components/RemoteSidebar.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import React, {useState} from 'react';
|
||||||
|
|
||||||
|
import {Button, Form, Modal} from 'react-bootstrap';
|
||||||
|
|
||||||
|
const RemoteSidebar = ({api, restHost, restPort, onApiChange}) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<RestApiForm host={restHost} port={restPort} onApiChange={onApiChange}/>
|
||||||
|
<RemoteDBForm api={api}/>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const RestApiForm = ({host, port, onApiChange}) => {
|
||||||
|
const [show, setShow] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setShow(false);
|
||||||
|
let form = e.target;
|
||||||
|
onApiChange({host: form.elements.host.value, port: form.elements.port.value});
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClick = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setShow(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mb-2 font-weight-light text-break">
|
||||||
|
<a href="/rest-api" className="nav-link px-2" onClick={handleClick}>
|
||||||
|
Rest API<br/>
|
||||||
|
{host && host + ':' + port}
|
||||||
|
</a>
|
||||||
|
<ModalWindow title="Rest API" show={show} onHide={() => setShow(false)}>
|
||||||
|
<Form onSubmit={handleSubmit}>
|
||||||
|
<Input label="Host" defaultValue={host}/>
|
||||||
|
<Input label="Port" defaultValue={port}/>
|
||||||
|
<Button type="submit">Submit</Button>
|
||||||
|
</Form>
|
||||||
|
</ModalWindow>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const get = (api, setHost, setPort) => {
|
||||||
|
const lookupSuccess = (response) => {
|
||||||
|
setHost(response.data.host);
|
||||||
|
setPort(response.data.port);
|
||||||
|
}
|
||||||
|
const lookupFail = (error) => {
|
||||||
|
setHost(() => {
|
||||||
|
throw error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return api.getRemoteDB().then(lookupSuccess).catch(lookupFail);
|
||||||
|
}
|
||||||
|
|
||||||
|
const set = (host, port, api, setHost, setPort) => {
|
||||||
|
const lookupSuccess = () => {
|
||||||
|
setHost(host);
|
||||||
|
setPort(port);
|
||||||
|
};
|
||||||
|
const lookupFail = (error) => {
|
||||||
|
setHost(() => {
|
||||||
|
throw error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return api.setRemoteDB(host, port).then(lookupSuccess).catch(lookupFail);
|
||||||
|
}
|
||||||
|
|
||||||
|
const RemoteDBForm = ({api}) => {
|
||||||
|
const [host, setHost] = useState('');
|
||||||
|
const [port, setPort] = useState('');
|
||||||
|
const [show, setShow] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
const form = e.target;
|
||||||
|
set(form.elements.host.value, form.elements.port.value, api, setHost, setPort)
|
||||||
|
setShow(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClick = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setShow(true)
|
||||||
|
get(api, setHost, setPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="pl-2 mb-2 font-weight-light text-break">
|
||||||
|
<a href="/remote-db" onClick={handleClick}>
|
||||||
|
Remote DB<br/>
|
||||||
|
{host && host + ':' + port}
|
||||||
|
</a>
|
||||||
|
<ModalWindow title="Remote DB" show={show} onHide={() => setShow(false)}>
|
||||||
|
<Form onSubmit={handleSubmit}>
|
||||||
|
<Input label="Host" defaultValue={host}/>
|
||||||
|
<Input label="Port" defaultValue={port}/>
|
||||||
|
<Button type="submit">Submit</Button>
|
||||||
|
</Form>
|
||||||
|
</ModalWindow>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Input = ({label, ...props}) => (
|
||||||
|
<Form.Group controlId={label.toLowerCase()}>
|
||||||
|
<Form.Label>{label}</Form.Label>
|
||||||
|
<Form.Control
|
||||||
|
type="text"
|
||||||
|
placeholder={label}
|
||||||
|
aria-describedby={"addon" + label.toLowerCase()}
|
||||||
|
name={label.toLowerCase()}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
)
|
||||||
|
|
||||||
|
const ModalWindow = ({children, title, ...props}) => (
|
||||||
|
<Modal {...props}>
|
||||||
|
<Modal.Header>
|
||||||
|
<Modal.Title>{title}</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
{children}
|
||||||
|
</Modal.Body>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default RemoteSidebar;
|
@ -142,7 +142,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if ctx.Config.RemoteDbListenAddress != "" {
|
if ctx.Config.RemoteDbListenAddress != "" {
|
||||||
if casted, ok := chainDb.(ethdb.KV); ok {
|
if casted, ok := chainDb.(ethdb.HasKV); ok {
|
||||||
remotedbserver.StartDeprecated(casted, ctx.Config.RemoteDbListenAddress)
|
remotedbserver.StartDeprecated(casted, ctx.Config.RemoteDbListenAddress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,22 +2,6 @@
|
|||||||
|
|
||||||
To build 1 key-value abstraction on top of Bolt, Badger and RemoteDB (our own read-only TCP protocol for key-value databases).
|
To build 1 key-value abstraction on top of Bolt, Badger and RemoteDB (our own read-only TCP protocol for key-value databases).
|
||||||
|
|
||||||
## Vision:
|
|
||||||
|
|
||||||
Ethereum gives users a powerful resource (which is hard to give) which is not explicitely priced -
|
|
||||||
transaction atomicity and "serialisable" isolation (the highest level of isolation you can get in the databases).
|
|
||||||
Which means that transaction does not even need to declare in advance what it wants to lock, the entire
|
|
||||||
state is deemed "locked" for its execution. I wonder if the weaker isolation models would make sense.
|
|
||||||
For example, ["read committed"](https://en.wikipedia.org/wiki/Isolation_(database_systems)#Read_committed)
|
|
||||||
|
|
||||||
with the weaker isolation, you might be able to split any transaction into smaller parts, each of which
|
|
||||||
does not perform any Dynamic State Access (I have no proof of that though).
|
|
||||||
|
|
||||||
It is similar to Tendermint strategy, but even more granular. You can view it as a support for "continuations".
|
|
||||||
Transactions starts, and whenever it hits dynamic access, its execution stops, gas is charged, and the continuation
|
|
||||||
is added to the state. Then, transaction can be resumed, because by committing to some continuation, it makes its
|
|
||||||
next dynamic state access static.
|
|
||||||
|
|
||||||
## Design principles:
|
## Design principles:
|
||||||
- No internal copies/allocations - all must be delegated to user.
|
- No internal copies/allocations - all must be delegated to user.
|
||||||
Make it part of contract - written clearly in docs, because it's unsafe (unsafe to put slice to DB and then change it).
|
Make it part of contract - written clearly in docs, because it's unsafe (unsafe to put slice to DB and then change it).
|
||||||
@ -25,7 +9,47 @@ Known problems: mutation.Put does copy internally.
|
|||||||
- Low-level API: as close to original Bolt/Badger as possible.
|
- Low-level API: as close to original Bolt/Badger as possible.
|
||||||
- Expose concept of transaction - app-level code can .Rollback() or .Commit() at once.
|
- Expose concept of transaction - app-level code can .Rollback() or .Commit() at once.
|
||||||
|
|
||||||
## Abstraction to support:
|
## Result interface:
|
||||||
|
|
||||||
|
```
|
||||||
|
type DB interface {
|
||||||
|
View(ctx context.Context, f func(tx Tx) error) (err error)
|
||||||
|
Update(ctx context.Context, f func(tx Tx) error) (err error)
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tx interface {
|
||||||
|
Bucket(name []byte) Bucket
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bucket interface {
|
||||||
|
Get(key []byte) (val []byte, err error)
|
||||||
|
Put(key []byte, value []byte) error
|
||||||
|
Delete(key []byte) error
|
||||||
|
Cursor() Cursor
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cursor interface {
|
||||||
|
Prefix(v []byte) Cursor
|
||||||
|
MatchBits(uint) Cursor
|
||||||
|
Prefetch(v uint) Cursor
|
||||||
|
NoValues() NoValuesCursor
|
||||||
|
|
||||||
|
First() ([]byte, []byte, error)
|
||||||
|
Seek(seek []byte) ([]byte, []byte, error)
|
||||||
|
Next() ([]byte, []byte, error)
|
||||||
|
Walk(walker func(k, v []byte) (bool, error)) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type NoValuesCursor interface {
|
||||||
|
First() ([]byte, uint64, error)
|
||||||
|
Seek(seek []byte) ([]byte, uint64, error)
|
||||||
|
Next() ([]byte, uint64, error)
|
||||||
|
Walk(walker func(k []byte, vSize uint64) (bool, error)) error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rationale and Features list:
|
||||||
|
|
||||||
#### Buckets concept:
|
#### Buckets concept:
|
||||||
- Bucket is an interface, can’t be nil, can't return error
|
- Bucket is an interface, can’t be nil, can't return error
|
||||||
@ -78,48 +102,3 @@ Known problems: mutation.Put does copy internally.
|
|||||||
- Monotonic int DB.GetSequence
|
- Monotonic int DB.GetSequence
|
||||||
- Nested Buckets
|
- Nested Buckets
|
||||||
- Backups, tx.WriteTo
|
- Backups, tx.WriteTo
|
||||||
|
|
||||||
## Result interface:
|
|
||||||
|
|
||||||
```
|
|
||||||
type DB interface {
|
|
||||||
View(ctx context.Context, f func(tx Tx) error) (err error)
|
|
||||||
Update(ctx context.Context, f func(tx Tx) error) (err error)
|
|
||||||
Close() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type Tx interface {
|
|
||||||
Bucket(name []byte) Bucket
|
|
||||||
}
|
|
||||||
|
|
||||||
type Bucket interface {
|
|
||||||
Get(key []byte) (val []byte, err error)
|
|
||||||
Put(key []byte, value []byte) error
|
|
||||||
Delete(key []byte) error
|
|
||||||
Cursor() Cursor
|
|
||||||
}
|
|
||||||
|
|
||||||
type Cursor interface {
|
|
||||||
Prefix(v []byte) Cursor
|
|
||||||
From(v []byte) Cursor
|
|
||||||
MatchBits(uint) Cursor
|
|
||||||
Prefetch(v uint) Cursor
|
|
||||||
NoValues() Cursor
|
|
||||||
|
|
||||||
First() ([]byte, []byte, error)
|
|
||||||
Seek(seek []byte) ([]byte, []byte, error)
|
|
||||||
Next() ([]byte, []byte, error)
|
|
||||||
FirstKey() ([]byte, uint64, error)
|
|
||||||
SeekKey(seek []byte) ([]byte, uint64, error)
|
|
||||||
NextKey() ([]byte, uint64, error)
|
|
||||||
|
|
||||||
Walk(walker func(k, v []byte) (bool, error)) error
|
|
||||||
WalkKeys(walker func(k []byte, vSize uint64) (bool, error)) error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Naming:
|
|
||||||
- `Iter` shorter `Cursor` shorter `Iterator`
|
|
||||||
- `Opts` shorter `Options`
|
|
||||||
- `Walk` shorter `ForEach`
|
|
||||||
|
|
||||||
|
@ -1,678 +0,0 @@
|
|||||||
package ethdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/dgraph-io/badger/v2"
|
|
||||||
"github.com/ledgerwatch/bolt"
|
|
||||||
"github.com/ledgerwatch/turbo-geth/common/dbutils"
|
|
||||||
"github.com/ledgerwatch/turbo-geth/ethdb/remote"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DB interface {
|
|
||||||
View(ctx context.Context, f func(tx Tx) error) (err error)
|
|
||||||
Update(ctx context.Context, f func(tx Tx) error) (err error)
|
|
||||||
Close() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type Tx interface {
|
|
||||||
Bucket(name []byte) Bucket
|
|
||||||
}
|
|
||||||
|
|
||||||
type Bucket interface {
|
|
||||||
Get(key []byte) (val []byte, err error)
|
|
||||||
Put(key []byte, value []byte) error
|
|
||||||
Delete(key []byte) error
|
|
||||||
Cursor() Cursor
|
|
||||||
}
|
|
||||||
|
|
||||||
type Cursor interface {
|
|
||||||
Prefix(v []byte) Cursor
|
|
||||||
From(v []byte) Cursor
|
|
||||||
MatchBits(uint) Cursor
|
|
||||||
Prefetch(v uint) Cursor
|
|
||||||
NoValues() Cursor
|
|
||||||
|
|
||||||
First() ([]byte, []byte, error)
|
|
||||||
Seek(seek []byte) ([]byte, []byte, error)
|
|
||||||
Next() ([]byte, []byte, error)
|
|
||||||
FirstKey() ([]byte, uint64, error)
|
|
||||||
SeekKey(seek []byte) ([]byte, uint64, error)
|
|
||||||
NextKey() ([]byte, uint64, error)
|
|
||||||
|
|
||||||
Walk(walker func(k, v []byte) (bool, error)) error
|
|
||||||
WalkKeys(walker func(k []byte, vSize uint64) (bool, error)) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type DbProvider uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
Bolt DbProvider = iota
|
|
||||||
Badger
|
|
||||||
Remote
|
|
||||||
)
|
|
||||||
|
|
||||||
const DefaultProvider = Bolt
|
|
||||||
|
|
||||||
type Options struct {
|
|
||||||
provider DbProvider
|
|
||||||
|
|
||||||
Remote remote.DbOpts
|
|
||||||
Bolt *bolt.Options
|
|
||||||
Badger badger.Options
|
|
||||||
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBolt() Options {
|
|
||||||
opts := Options{provider: Bolt, Bolt: bolt.DefaultOptions}
|
|
||||||
return opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBadger() Options {
|
|
||||||
opts := Options{provider: Badger, Badger: badger.DefaultOptions("")}
|
|
||||||
return opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRemote() Options {
|
|
||||||
opts := Options{provider: Badger, Remote: remote.DefaultOpts}
|
|
||||||
return opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMemDb() Options {
|
|
||||||
return Opts().InMem(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProviderOpts(provider DbProvider) Options {
|
|
||||||
switch provider {
|
|
||||||
case Bolt:
|
|
||||||
return NewBolt()
|
|
||||||
case Badger:
|
|
||||||
return NewBadger()
|
|
||||||
case Remote:
|
|
||||||
return NewRemote()
|
|
||||||
default:
|
|
||||||
panic("unknown db provider: " + strconv.Itoa(int(provider)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Opts() Options {
|
|
||||||
return ProviderOpts(DefaultProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (opts Options) Path(path string) Options {
|
|
||||||
opts.path = path
|
|
||||||
switch opts.provider {
|
|
||||||
case Bolt:
|
|
||||||
// nothing to do
|
|
||||||
case Badger:
|
|
||||||
opts.Badger = opts.Badger.WithDir(path).WithValueDir(path)
|
|
||||||
case Remote:
|
|
||||||
opts.Remote = opts.Remote.Addr(path)
|
|
||||||
}
|
|
||||||
return opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (opts Options) InMem(val bool) Options {
|
|
||||||
switch opts.provider {
|
|
||||||
case Bolt:
|
|
||||||
opts.Bolt.MemOnly = val
|
|
||||||
case Badger:
|
|
||||||
opts.Badger = opts.Badger.WithInMemory(val)
|
|
||||||
case Remote:
|
|
||||||
panic("not supported")
|
|
||||||
}
|
|
||||||
return opts
|
|
||||||
}
|
|
||||||
|
|
||||||
type allDB struct {
|
|
||||||
opts Options
|
|
||||||
bolt *bolt.DB
|
|
||||||
badger *badger.DB
|
|
||||||
remote *remote.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
var buckets = [][]byte{
|
|
||||||
dbutils.IntermediateTrieHashBucket,
|
|
||||||
dbutils.AccountsBucket,
|
|
||||||
}
|
|
||||||
|
|
||||||
func (opts Options) Open(ctx context.Context) (db *allDB, err error) {
|
|
||||||
return Open(ctx, opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Open(ctx context.Context, opts Options) (db *allDB, err error) {
|
|
||||||
db = &allDB{opts: opts}
|
|
||||||
|
|
||||||
switch db.opts.provider {
|
|
||||||
case Bolt:
|
|
||||||
db.bolt, err = bolt.Open(opts.path, 0600, opts.Bolt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = db.bolt.Update(func(tx *bolt.Tx) error {
|
|
||||||
for _, name := range buckets {
|
|
||||||
_, createErr := tx.CreateBucketIfNotExists(name, false)
|
|
||||||
if createErr != nil {
|
|
||||||
return createErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
case Badger:
|
|
||||||
db.badger, err = badger.Open(opts.Badger)
|
|
||||||
case Remote:
|
|
||||||
db.remote, err = remote.Open(ctx, opts.Remote)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return db, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes DB
|
|
||||||
// All transactions must be closed before closing the database.
|
|
||||||
func (db *allDB) Close() error {
|
|
||||||
switch db.opts.provider {
|
|
||||||
case Bolt:
|
|
||||||
return db.bolt.Close()
|
|
||||||
case Badger:
|
|
||||||
return db.badger.Close()
|
|
||||||
case Remote:
|
|
||||||
return db.remote.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type tx struct {
|
|
||||||
ctx context.Context
|
|
||||||
db *allDB
|
|
||||||
|
|
||||||
bolt *bolt.Tx
|
|
||||||
badger *badger.Txn
|
|
||||||
remote *remote.Tx
|
|
||||||
|
|
||||||
badgerIterators []*badger.Iterator
|
|
||||||
}
|
|
||||||
|
|
||||||
type bucket struct {
|
|
||||||
tx *tx
|
|
||||||
|
|
||||||
bolt *bolt.Bucket
|
|
||||||
badgerPrefix []byte
|
|
||||||
nameLen uint
|
|
||||||
remote *remote.Bucket
|
|
||||||
}
|
|
||||||
|
|
||||||
type cursor struct {
|
|
||||||
ctx context.Context
|
|
||||||
bucket bucket
|
|
||||||
provider DbProvider
|
|
||||||
prefix []byte
|
|
||||||
|
|
||||||
remoteOpts remote.CursorOpts
|
|
||||||
badgerOpts badger.IteratorOptions
|
|
||||||
|
|
||||||
bolt *bolt.Cursor
|
|
||||||
badger *badger.Iterator
|
|
||||||
remote *remote.Cursor
|
|
||||||
|
|
||||||
k []byte
|
|
||||||
v []byte
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *allDB) View(ctx context.Context, f func(tx Tx) error) (err error) {
|
|
||||||
t := &tx{db: db, ctx: ctx}
|
|
||||||
switch db.opts.provider {
|
|
||||||
case Bolt:
|
|
||||||
return db.bolt.View(func(tx *bolt.Tx) error {
|
|
||||||
defer t.cleanup()
|
|
||||||
t.bolt = tx
|
|
||||||
return f(t)
|
|
||||||
})
|
|
||||||
case Badger:
|
|
||||||
return db.badger.View(func(tx *badger.Txn) error {
|
|
||||||
defer t.cleanup()
|
|
||||||
t.badger = tx
|
|
||||||
return f(t)
|
|
||||||
})
|
|
||||||
case Remote:
|
|
||||||
return db.remote.View(ctx, func(tx *remote.Tx) error {
|
|
||||||
t.remote = tx
|
|
||||||
return f(t)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *allDB) Update(ctx context.Context, f func(tx Tx) error) (err error) {
|
|
||||||
t := &tx{db: db, ctx: ctx}
|
|
||||||
switch db.opts.provider {
|
|
||||||
case Bolt:
|
|
||||||
return db.bolt.Update(func(tx *bolt.Tx) error {
|
|
||||||
defer t.cleanup()
|
|
||||||
t.bolt = tx
|
|
||||||
return f(t)
|
|
||||||
})
|
|
||||||
case Badger:
|
|
||||||
return db.badger.Update(func(tx *badger.Txn) error {
|
|
||||||
defer t.cleanup()
|
|
||||||
t.badger = tx
|
|
||||||
return f(t)
|
|
||||||
})
|
|
||||||
case Remote:
|
|
||||||
return fmt.Errorf("remote db provider doesn't support .Update method")
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tx *tx) Bucket(name []byte) Bucket {
|
|
||||||
b := bucket{tx: tx, nameLen: uint(len(name))}
|
|
||||||
switch tx.db.opts.provider {
|
|
||||||
case Bolt:
|
|
||||||
b.bolt = tx.bolt.Bucket(name)
|
|
||||||
case Badger:
|
|
||||||
b.badgerPrefix = name
|
|
||||||
case Remote:
|
|
||||||
b.remote = tx.remote.Bucket(name)
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tx *tx) cleanup() {
|
|
||||||
switch tx.db.opts.provider {
|
|
||||||
case Bolt:
|
|
||||||
// nothing to cleanup
|
|
||||||
case Badger:
|
|
||||||
for _, it := range tx.badgerIterators {
|
|
||||||
it.Close()
|
|
||||||
}
|
|
||||||
case Remote:
|
|
||||||
// nothing to cleanup
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cursor) Prefix(v []byte) Cursor {
|
|
||||||
c.prefix = v
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cursor) From(v []byte) Cursor {
|
|
||||||
panic("not implemented yet")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cursor) MatchBits(n uint) Cursor {
|
|
||||||
panic("not implemented yet")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cursor) Prefetch(v uint) Cursor {
|
|
||||||
switch c.provider {
|
|
||||||
case Bolt:
|
|
||||||
// nothing to do
|
|
||||||
case Badger:
|
|
||||||
c.badgerOpts.PrefetchSize = int(v)
|
|
||||||
case Remote:
|
|
||||||
c.remoteOpts.PrefetchSize(uint64(v))
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cursor) NoValues() Cursor {
|
|
||||||
switch c.provider {
|
|
||||||
case Bolt:
|
|
||||||
// nothing to do
|
|
||||||
case Badger:
|
|
||||||
c.badgerOpts.PrefetchValues = false
|
|
||||||
case Remote:
|
|
||||||
c.remoteOpts.PrefetchValues(false)
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b bucket) Get(key []byte) (val []byte, err error) {
|
|
||||||
select {
|
|
||||||
case <-b.tx.ctx.Done():
|
|
||||||
return nil, b.tx.ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
switch b.tx.db.opts.provider {
|
|
||||||
case Bolt:
|
|
||||||
val, _ = b.bolt.Get(key)
|
|
||||||
case Badger:
|
|
||||||
var item *badger.Item
|
|
||||||
b.badgerPrefix = append(b.badgerPrefix[:b.nameLen], key...)
|
|
||||||
item, err = b.tx.badger.Get(b.badgerPrefix)
|
|
||||||
if item != nil {
|
|
||||||
val, err = item.ValueCopy(nil) // can improve this by using pool
|
|
||||||
}
|
|
||||||
case Remote:
|
|
||||||
val, err = b.remote.Get(key)
|
|
||||||
}
|
|
||||||
return val, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b bucket) Put(key []byte, value []byte) error {
|
|
||||||
select {
|
|
||||||
case <-b.tx.ctx.Done():
|
|
||||||
return b.tx.ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
switch b.tx.db.opts.provider {
|
|
||||||
case Bolt:
|
|
||||||
return b.bolt.Put(key, value)
|
|
||||||
case Badger:
|
|
||||||
b.badgerPrefix = append(b.badgerPrefix[:b.nameLen], key...)
|
|
||||||
return b.tx.badger.Set(b.badgerPrefix, value)
|
|
||||||
case Remote:
|
|
||||||
panic("not supported")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b bucket) Delete(key []byte) error {
|
|
||||||
select {
|
|
||||||
case <-b.tx.ctx.Done():
|
|
||||||
return b.tx.ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
switch b.tx.db.opts.provider {
|
|
||||||
case Bolt:
|
|
||||||
return b.bolt.Delete(key)
|
|
||||||
case Badger:
|
|
||||||
b.badgerPrefix = append(b.badgerPrefix[:b.nameLen], key...)
|
|
||||||
return b.tx.badger.Delete(b.badgerPrefix)
|
|
||||||
case Remote:
|
|
||||||
panic("not supported")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b bucket) Cursor() Cursor {
|
|
||||||
c := &cursor{bucket: b, ctx: b.tx.ctx, provider: b.tx.db.opts.provider}
|
|
||||||
switch c.provider {
|
|
||||||
case Bolt:
|
|
||||||
// nothing to do
|
|
||||||
case Badger:
|
|
||||||
c.badgerOpts = badger.DefaultIteratorOptions
|
|
||||||
b.badgerPrefix = append(b.badgerPrefix[:b.nameLen], c.prefix...) // set bucket
|
|
||||||
c.badgerOpts.Prefix = b.badgerPrefix // set bucket
|
|
||||||
case Remote:
|
|
||||||
c.remoteOpts = remote.DefaultCursorOpts
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cursor) initCursor() {
|
|
||||||
switch c.provider {
|
|
||||||
case Bolt:
|
|
||||||
if c.bolt != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.bolt = c.bucket.bolt.Cursor()
|
|
||||||
case Badger:
|
|
||||||
if c.badger != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.badger = c.bucket.tx.badger.NewIterator(c.badgerOpts)
|
|
||||||
// add to auto-cleanup on end of transactions
|
|
||||||
if c.bucket.tx.badgerIterators == nil {
|
|
||||||
c.bucket.tx.badgerIterators = make([]*badger.Iterator, 0, 1)
|
|
||||||
}
|
|
||||||
c.bucket.tx.badgerIterators = append(c.bucket.tx.badgerIterators, c.badger)
|
|
||||||
case Remote:
|
|
||||||
if c.remote != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.remote = c.bucket.remote.Cursor(c.remoteOpts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cursor) First() ([]byte, []byte, error) {
|
|
||||||
c.initCursor()
|
|
||||||
|
|
||||||
switch c.provider {
|
|
||||||
case Bolt:
|
|
||||||
if c.prefix != nil {
|
|
||||||
c.k, c.v = c.bolt.Seek(c.prefix)
|
|
||||||
} else {
|
|
||||||
c.k, c.v = c.bolt.First()
|
|
||||||
}
|
|
||||||
case Badger:
|
|
||||||
c.badger.Rewind()
|
|
||||||
if !c.badger.Valid() {
|
|
||||||
c.k = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
item := c.badger.Item()
|
|
||||||
c.k = item.Key()[c.bucket.nameLen:]
|
|
||||||
if c.badgerOpts.PrefetchValues {
|
|
||||||
c.v, c.err = item.ValueCopy(c.v) // bech show: using .ValueCopy on same buffer has same speed as item.Value()
|
|
||||||
}
|
|
||||||
case Remote:
|
|
||||||
if c.prefix != nil {
|
|
||||||
c.k, c.v, c.err = c.remote.Seek(c.prefix)
|
|
||||||
} else {
|
|
||||||
c.k, c.v, c.err = c.remote.First()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c.k, c.v, c.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cursor) Seek(seek []byte) ([]byte, []byte, error) {
|
|
||||||
select {
|
|
||||||
case <-c.ctx.Done():
|
|
||||||
return nil, nil, c.ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
c.initCursor()
|
|
||||||
|
|
||||||
switch c.provider {
|
|
||||||
case Bolt:
|
|
||||||
c.k, c.v = c.bolt.Seek(seek)
|
|
||||||
case Badger:
|
|
||||||
c.bucket.badgerPrefix = append(c.bucket.badgerPrefix[:c.bucket.nameLen], seek...)
|
|
||||||
c.badger.Seek(c.bucket.badgerPrefix)
|
|
||||||
if !c.badger.Valid() {
|
|
||||||
c.k = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
item := c.badger.Item()
|
|
||||||
c.k = item.Key()[c.bucket.nameLen:]
|
|
||||||
if c.badgerOpts.PrefetchValues {
|
|
||||||
c.v, c.err = item.ValueCopy(c.v)
|
|
||||||
}
|
|
||||||
case Remote:
|
|
||||||
c.k, c.v, c.err = c.remote.Seek(seek)
|
|
||||||
}
|
|
||||||
return c.k, c.v, c.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cursor) Next() ([]byte, []byte, error) {
|
|
||||||
select {
|
|
||||||
case <-c.ctx.Done():
|
|
||||||
return nil, nil, c.ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
switch c.provider {
|
|
||||||
case Bolt:
|
|
||||||
c.k, c.v = c.bolt.Next()
|
|
||||||
if c.prefix != nil && !bytes.HasPrefix(c.k, c.prefix) {
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
case Badger:
|
|
||||||
c.badger.Next()
|
|
||||||
if !c.badger.Valid() {
|
|
||||||
c.k = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
item := c.badger.Item()
|
|
||||||
c.k = item.Key()[c.bucket.nameLen:]
|
|
||||||
if c.badgerOpts.PrefetchValues {
|
|
||||||
c.v, c.err = item.ValueCopy(c.v)
|
|
||||||
}
|
|
||||||
case Remote:
|
|
||||||
c.k, c.v, c.err = c.remote.Next()
|
|
||||||
if c.err != nil {
|
|
||||||
return nil, nil, c.err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.prefix != nil && !bytes.HasPrefix(c.k, c.prefix) {
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c.k, c.v, c.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cursor) FirstKey() ([]byte, uint64, error) {
|
|
||||||
c.initCursor()
|
|
||||||
|
|
||||||
var vSize uint64
|
|
||||||
switch c.provider {
|
|
||||||
case Bolt:
|
|
||||||
var v []byte
|
|
||||||
if c.prefix != nil {
|
|
||||||
c.k, v = c.bolt.Seek(c.prefix)
|
|
||||||
} else {
|
|
||||||
c.k, v = c.bolt.First()
|
|
||||||
}
|
|
||||||
vSize = uint64(len(v))
|
|
||||||
case Badger:
|
|
||||||
c.badger.Rewind()
|
|
||||||
if !c.badger.Valid() {
|
|
||||||
c.k = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
item := c.badger.Item()
|
|
||||||
c.k = item.Key()[c.bucket.nameLen:]
|
|
||||||
vSize = uint64(item.ValueSize())
|
|
||||||
case Remote:
|
|
||||||
var vIsEmpty bool
|
|
||||||
if c.prefix != nil {
|
|
||||||
c.k, vIsEmpty, c.err = c.remote.SeekKey(c.prefix)
|
|
||||||
} else {
|
|
||||||
c.k, vIsEmpty, c.err = c.remote.FirstKey()
|
|
||||||
}
|
|
||||||
if !vIsEmpty {
|
|
||||||
vSize = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c.k, vSize, c.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cursor) SeekKey(seek []byte) ([]byte, uint64, error) {
|
|
||||||
select {
|
|
||||||
case <-c.ctx.Done():
|
|
||||||
return nil, 0, c.ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
c.initCursor()
|
|
||||||
|
|
||||||
var vSize uint64
|
|
||||||
switch c.provider {
|
|
||||||
case Bolt:
|
|
||||||
var v []byte
|
|
||||||
c.k, v = c.bolt.Seek(seek)
|
|
||||||
vSize = uint64(len(v))
|
|
||||||
case Badger:
|
|
||||||
c.bucket.badgerPrefix = append(c.bucket.badgerPrefix[:c.bucket.nameLen], seek...)
|
|
||||||
c.badger.Seek(c.bucket.badgerPrefix)
|
|
||||||
if !c.badger.Valid() {
|
|
||||||
c.k = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
item := c.badger.Item()
|
|
||||||
c.k = item.Key()[c.bucket.nameLen:]
|
|
||||||
vSize = uint64(item.ValueSize())
|
|
||||||
case Remote:
|
|
||||||
var vIsEmpty bool
|
|
||||||
c.k, vIsEmpty, c.err = c.remote.SeekKey(seek)
|
|
||||||
if !vIsEmpty {
|
|
||||||
vSize = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c.k, vSize, c.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cursor) NextKey() ([]byte, uint64, error) {
|
|
||||||
select {
|
|
||||||
case <-c.ctx.Done():
|
|
||||||
return nil, 0, c.ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
var vSize uint64
|
|
||||||
switch c.provider {
|
|
||||||
case Bolt:
|
|
||||||
var v []byte
|
|
||||||
c.k, v = c.bolt.Next()
|
|
||||||
vSize = uint64(len(v))
|
|
||||||
if c.prefix != nil && !bytes.HasPrefix(c.k, c.prefix) {
|
|
||||||
return nil, 0, nil
|
|
||||||
}
|
|
||||||
case Badger:
|
|
||||||
c.badger.Next()
|
|
||||||
if !c.badger.Valid() {
|
|
||||||
c.k = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
item := c.badger.Item()
|
|
||||||
c.k = item.Key()[c.bucket.nameLen:]
|
|
||||||
vSize = uint64(item.ValueSize())
|
|
||||||
case Remote:
|
|
||||||
var vIsEmpty bool
|
|
||||||
c.k, vIsEmpty, c.err = c.remote.NextKey()
|
|
||||||
if !vIsEmpty {
|
|
||||||
vSize = 1
|
|
||||||
}
|
|
||||||
if c.err != nil {
|
|
||||||
return nil, 0, c.err
|
|
||||||
}
|
|
||||||
if c.prefix != nil && !bytes.HasPrefix(c.k, c.prefix) {
|
|
||||||
return nil, 0, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c.k, vSize, c.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cursor) Walk(walker func(k, v []byte) (bool, error)) error {
|
|
||||||
for k, v, err := c.First(); k != nil || err != nil; k, v, err = c.Next() {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ok, err := walker(k, v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cursor) WalkKeys(walker func(k []byte, vSize uint64) (bool, error)) error {
|
|
||||||
for k, vSize, err := c.FirstKey(); k != nil || err != nil; k, vSize, err = c.NextKey() {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ok, err := walker(k, vSize)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -16,8 +16,8 @@ import (
|
|||||||
|
|
||||||
var boltOriginDb *bolt.DB
|
var boltOriginDb *bolt.DB
|
||||||
var badgerOriginDb *badger.DB
|
var badgerOriginDb *badger.DB
|
||||||
var boltDb ethdb.DB
|
var boltDb ethdb.KV
|
||||||
var badgerDb ethdb.DB
|
var badgerDb ethdb.KV
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
setupDatabases()
|
setupDatabases()
|
||||||
@ -134,9 +134,7 @@ func BenchmarkCursor(b *testing.B) {
|
|||||||
b.Run("abstract bolt", func(b *testing.B) {
|
b.Run("abstract bolt", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
if err := boltDb.View(ctx, func(tx ethdb.Tx) error {
|
if err := boltDb.View(ctx, func(tx ethdb.Tx) error {
|
||||||
bucket := tx.Bucket(dbutils.AccountsBucket)
|
c := tx.Bucket(dbutils.AccountsBucket).Cursor()
|
||||||
c := bucket.Cursor()
|
|
||||||
|
|
||||||
for k, v, err := c.First(); k != nil || err != nil; k, v, err = c.Next() {
|
for k, v, err := c.First(); k != nil || err != nil; k, v, err = c.Next() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -123,7 +123,7 @@ type DbWithPendingMutations interface {
|
|||||||
BatchSize() int
|
BatchSize() int
|
||||||
}
|
}
|
||||||
|
|
||||||
type KV interface {
|
type HasKV interface {
|
||||||
KV() *bolt.DB
|
KV() *bolt.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
175
ethdb/kv_abstract.go
Normal file
175
ethdb/kv_abstract.go
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
package ethdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/dgraph-io/badger/v2"
|
||||||
|
"github.com/ledgerwatch/bolt"
|
||||||
|
"github.com/ledgerwatch/turbo-geth/common/dbutils"
|
||||||
|
"github.com/ledgerwatch/turbo-geth/ethdb/remote"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KV interface {
|
||||||
|
Options() Options
|
||||||
|
View(ctx context.Context, f func(tx Tx) error) (err error)
|
||||||
|
Update(ctx context.Context, f func(tx Tx) error) (err error)
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tx interface {
|
||||||
|
Bucket(name []byte) Bucket
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bucket interface {
|
||||||
|
Get(key []byte) (val []byte, err error)
|
||||||
|
Put(key []byte, value []byte) error
|
||||||
|
Delete(key []byte) error
|
||||||
|
Cursor() Cursor
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cursor interface {
|
||||||
|
Prefix(v []byte) Cursor
|
||||||
|
MatchBits(uint) Cursor
|
||||||
|
Prefetch(v uint) Cursor
|
||||||
|
NoValues() NoValuesCursor
|
||||||
|
|
||||||
|
First() ([]byte, []byte, error)
|
||||||
|
Seek(seek []byte) ([]byte, []byte, error)
|
||||||
|
Next() ([]byte, []byte, error)
|
||||||
|
Walk(walker func(k, v []byte) (bool, error)) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type NoValuesCursor interface {
|
||||||
|
First() ([]byte, uint64, error)
|
||||||
|
Seek(seek []byte) ([]byte, uint64, error)
|
||||||
|
Next() ([]byte, uint64, error)
|
||||||
|
Walk(walker func(k []byte, vSize uint64) (bool, error)) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type DbProvider uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
Bolt DbProvider = iota
|
||||||
|
Badger
|
||||||
|
Remote
|
||||||
|
)
|
||||||
|
|
||||||
|
const DefaultProvider = Bolt
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
provider DbProvider
|
||||||
|
|
||||||
|
Remote remote.DbOpts
|
||||||
|
Bolt *bolt.Options
|
||||||
|
Badger badger.Options
|
||||||
|
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBolt() Options {
|
||||||
|
opts := Options{provider: Bolt, Bolt: bolt.DefaultOptions}
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBadger() Options {
|
||||||
|
opts := Options{provider: Badger, Badger: badger.DefaultOptions("")}
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRemote() Options {
|
||||||
|
opts := Options{provider: Remote, Remote: remote.DefaultOpts}
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMemDb() Options {
|
||||||
|
return Opts().InMem(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProviderOpts(provider DbProvider) Options {
|
||||||
|
switch provider {
|
||||||
|
case Bolt:
|
||||||
|
return NewBolt()
|
||||||
|
case Badger:
|
||||||
|
return NewBadger()
|
||||||
|
case Remote:
|
||||||
|
return NewRemote()
|
||||||
|
default:
|
||||||
|
panic("unknown db provider: " + strconv.Itoa(int(provider)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Opts() Options {
|
||||||
|
return ProviderOpts(DefaultProvider)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts Options) Path(path string) Options {
|
||||||
|
opts.path = path
|
||||||
|
switch opts.provider {
|
||||||
|
case Bolt:
|
||||||
|
// nothing to do
|
||||||
|
case Badger:
|
||||||
|
opts.Badger = opts.Badger.WithDir(path).WithValueDir(path)
|
||||||
|
case Remote:
|
||||||
|
opts.Remote = opts.Remote.Addr(path)
|
||||||
|
}
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts Options) InMem(val bool) Options {
|
||||||
|
switch opts.provider {
|
||||||
|
case Bolt:
|
||||||
|
opts.Bolt.MemOnly = val
|
||||||
|
case Badger:
|
||||||
|
opts.Badger = opts.Badger.WithInMemory(val)
|
||||||
|
case Remote:
|
||||||
|
panic("not supported")
|
||||||
|
}
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts Options) Open(ctx context.Context) (db KV, err error) {
|
||||||
|
return Open(ctx, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Open(ctx context.Context, opts Options) (KV, error) {
|
||||||
|
switch opts.provider {
|
||||||
|
case Bolt:
|
||||||
|
db := &BoltKV{opts: opts}
|
||||||
|
boltDB, err := bolt.Open(opts.path, 0600, opts.Bolt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
db.bolt = boltDB
|
||||||
|
err = db.bolt.Update(func(tx *bolt.Tx) error {
|
||||||
|
for _, name := range dbutils.Buckets {
|
||||||
|
_, createErr := tx.CreateBucketIfNotExists(name, false)
|
||||||
|
if createErr != nil {
|
||||||
|
return createErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return db, nil
|
||||||
|
case Badger:
|
||||||
|
db := &badgerDB{opts: opts}
|
||||||
|
badgerDB, err := badger.Open(opts.Badger)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
db.badger = badgerDB
|
||||||
|
return db, nil
|
||||||
|
case Remote:
|
||||||
|
db := &remoteDB{opts: opts}
|
||||||
|
remoteDb, err := remote.Open(ctx, opts.Remote)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
db.remote = remoteDb
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
panic("unknown db provider")
|
||||||
|
}
|
@ -17,7 +17,7 @@ func TestManagedTx(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
t.Run("Bolt", func(t *testing.T) {
|
t.Run("Bolt", func(t *testing.T) {
|
||||||
var db ethdb.DB
|
var db ethdb.KV
|
||||||
var errOpen error
|
var errOpen error
|
||||||
db, errOpen = ethdb.NewBolt().InMem(true).Open(ctx)
|
db, errOpen = ethdb.NewBolt().InMem(true).Open(ctx)
|
||||||
assert.NoError(t, errOpen)
|
assert.NoError(t, errOpen)
|
||||||
@ -52,7 +52,8 @@ func TestManagedTx(t *testing.T) {
|
|||||||
_ = v
|
_ = v
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, vSize, err := c.FirstKey(); k != nil || err != nil; k, vSize, err = c.NextKey() {
|
c2 := c.NoValues()
|
||||||
|
for k, vSize, err := c2.First(); k != nil || err != nil; k, vSize, err = c2.Next() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -73,7 +74,7 @@ func TestManagedTx(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Badger", func(t *testing.T) {
|
t.Run("Badger", func(t *testing.T) {
|
||||||
var db ethdb.DB
|
var db ethdb.KV
|
||||||
var errOpen error
|
var errOpen error
|
||||||
db, errOpen = ethdb.NewBadger().InMem(true).Open(ctx)
|
db, errOpen = ethdb.NewBadger().InMem(true).Open(ctx)
|
||||||
assert.NoError(t, errOpen)
|
assert.NoError(t, errOpen)
|
||||||
@ -113,7 +114,7 @@ func TestManagedTx(t *testing.T) {
|
|||||||
//c, err := tx.Bucket(dbutil.AccountBucket).CursorOpts().From(key).Cursor()
|
//c, err := tx.Bucket(dbutil.AccountBucket).CursorOpts().From(key).Cursor()
|
||||||
//
|
//
|
||||||
//c, err := b.Cursor(b.CursorOpts().From(key).MatchBits(common.HashLength * 8))
|
//c, err := b.Cursor(b.CursorOpts().From(key).MatchBits(common.HashLength * 8))
|
||||||
c := b.Cursor()
|
c := b.Cursor().NoValues()
|
||||||
|
|
||||||
for k, v, err := c.First(); k != nil || err != nil; k, v, err = c.Next() {
|
for k, v, err := c.First(); k != nil || err != nil; k, v, err = c.Next() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -122,7 +123,7 @@ func TestManagedTx(t *testing.T) {
|
|||||||
_ = v
|
_ = v
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, vSize, err := c.FirstKey(); k != nil || err != nil; k, vSize, err = c.NextKey() {
|
for k, vSize, err := c.First(); k != nil || err != nil; k, vSize, err = c.Next() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -160,7 +161,7 @@ func TestCancelTest(t *testing.T) {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Microsecond)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Microsecond)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
var db ethdb.DB
|
var db ethdb.KV
|
||||||
var errOpen error
|
var errOpen error
|
||||||
db, errOpen = ethdb.NewBolt().InMem(true).Open(ctx)
|
db, errOpen = ethdb.NewBolt().InMem(true).Open(ctx)
|
||||||
assert.NoError(t, errOpen)
|
assert.NoError(t, errOpen)
|
||||||
@ -186,7 +187,7 @@ func TestCancelTest(t *testing.T) {
|
|||||||
func TestFilterTest(t *testing.T) {
|
func TestFilterTest(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
var db ethdb.DB
|
var db ethdb.KV
|
||||||
var errOpen error
|
var errOpen error
|
||||||
db, errOpen = ethdb.NewBolt().InMem(true).Open(ctx)
|
db, errOpen = ethdb.NewBolt().InMem(true).Open(ctx)
|
||||||
assert.NoError(t, errOpen)
|
assert.NoError(t, errOpen)
|
||||||
@ -247,7 +248,7 @@ func TestUnmanagedTx(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
t.Run("Bolt", func(t *testing.T) {
|
t.Run("Bolt", func(t *testing.T) {
|
||||||
var db ethdb.DB
|
var db ethdb.KV
|
||||||
var errOpen error
|
var errOpen error
|
||||||
db, errOpen = ethdb.NewBolt().InMem(true).Open(ctx)
|
db, errOpen = ethdb.NewBolt().InMem(true).Open(ctx)
|
||||||
assert.NoError(t, errOpen)
|
assert.NoError(t, errOpen)
|
316
ethdb/kv_badger.go
Normal file
316
ethdb/kv_badger.go
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
package ethdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/dgraph-io/badger/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type badgerDB struct {
|
||||||
|
opts Options
|
||||||
|
badger *badger.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes BoltKV
|
||||||
|
// All transactions must be closed before closing the database.
|
||||||
|
func (db *badgerDB) Close() error {
|
||||||
|
return db.badger.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
type badgerTx struct {
|
||||||
|
ctx context.Context
|
||||||
|
db *badgerDB
|
||||||
|
|
||||||
|
badger *badger.Txn
|
||||||
|
badgerIterators []*badger.Iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
type badgerBucket struct {
|
||||||
|
tx *badgerTx
|
||||||
|
|
||||||
|
badgerPrefix []byte
|
||||||
|
nameLen uint
|
||||||
|
}
|
||||||
|
|
||||||
|
type badgerCursor struct {
|
||||||
|
ctx context.Context
|
||||||
|
bucket badgerBucket
|
||||||
|
prefix []byte
|
||||||
|
|
||||||
|
badgerOpts badger.IteratorOptions
|
||||||
|
|
||||||
|
badger *badger.Iterator
|
||||||
|
|
||||||
|
k []byte
|
||||||
|
v []byte
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *badgerDB) Options() Options {
|
||||||
|
return db.opts
|
||||||
|
}
|
||||||
|
func (db *badgerDB) View(ctx context.Context, f func(tx Tx) error) (err error) {
|
||||||
|
t := &badgerTx{db: db, ctx: ctx}
|
||||||
|
return db.badger.View(func(tx *badger.Txn) error {
|
||||||
|
defer t.cleanup()
|
||||||
|
t.badger = tx
|
||||||
|
return f(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *badgerDB) Update(ctx context.Context, f func(tx Tx) error) (err error) {
|
||||||
|
t := &badgerTx{db: db, ctx: ctx}
|
||||||
|
return db.badger.Update(func(tx *badger.Txn) error {
|
||||||
|
defer t.cleanup()
|
||||||
|
t.badger = tx
|
||||||
|
return f(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *badgerTx) Bucket(name []byte) Bucket {
|
||||||
|
b := badgerBucket{tx: tx, nameLen: uint(len(name))}
|
||||||
|
b.badgerPrefix = name
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *badgerTx) cleanup() {
|
||||||
|
for _, it := range tx.badgerIterators {
|
||||||
|
it.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *badgerCursor) Prefix(v []byte) Cursor {
|
||||||
|
c.prefix = v
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *badgerCursor) MatchBits(n uint) Cursor {
|
||||||
|
panic("not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *badgerCursor) Prefetch(v uint) Cursor {
|
||||||
|
c.badgerOpts.PrefetchSize = int(v)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *badgerCursor) NoValues() NoValuesCursor {
|
||||||
|
c.badgerOpts.PrefetchValues = false
|
||||||
|
return &badgerNoValuesCursor{badgerCursor: *c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b badgerBucket) Get(key []byte) (val []byte, err error) {
|
||||||
|
select {
|
||||||
|
case <-b.tx.ctx.Done():
|
||||||
|
return nil, b.tx.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
var item *badger.Item
|
||||||
|
b.badgerPrefix = append(b.badgerPrefix[:b.nameLen], key...)
|
||||||
|
item, err = b.tx.badger.Get(b.badgerPrefix)
|
||||||
|
if item != nil {
|
||||||
|
val, err = item.ValueCopy(nil) // can improve this by using pool
|
||||||
|
}
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b badgerBucket) Put(key []byte, value []byte) error {
|
||||||
|
select {
|
||||||
|
case <-b.tx.ctx.Done():
|
||||||
|
return b.tx.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
b.badgerPrefix = append(b.badgerPrefix[:b.nameLen], key...)
|
||||||
|
return b.tx.badger.Set(b.badgerPrefix, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b badgerBucket) Delete(key []byte) error {
|
||||||
|
select {
|
||||||
|
case <-b.tx.ctx.Done():
|
||||||
|
return b.tx.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
b.badgerPrefix = append(b.badgerPrefix[:b.nameLen], key...)
|
||||||
|
return b.tx.badger.Delete(b.badgerPrefix)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b badgerBucket) Cursor() Cursor {
|
||||||
|
c := &badgerCursor{bucket: b, ctx: b.tx.ctx}
|
||||||
|
// nothing to do
|
||||||
|
c.badgerOpts = badger.DefaultIteratorOptions
|
||||||
|
b.badgerPrefix = append(b.badgerPrefix[:b.nameLen], c.prefix...) // set bucket
|
||||||
|
c.badgerOpts.Prefix = b.badgerPrefix // set bucket
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *badgerCursor) initCursor() {
|
||||||
|
if c.badger != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.badger = c.bucket.tx.badger.NewIterator(c.badgerOpts)
|
||||||
|
// add to auto-cleanup on end of transactions
|
||||||
|
if c.bucket.tx.badgerIterators == nil {
|
||||||
|
c.bucket.tx.badgerIterators = make([]*badger.Iterator, 0, 1)
|
||||||
|
}
|
||||||
|
c.bucket.tx.badgerIterators = append(c.bucket.tx.badgerIterators, c.badger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *badgerCursor) First() ([]byte, []byte, error) {
|
||||||
|
c.initCursor()
|
||||||
|
|
||||||
|
c.badger.Rewind()
|
||||||
|
if !c.badger.Valid() {
|
||||||
|
c.k = nil
|
||||||
|
return c.k, c.v, c.err
|
||||||
|
}
|
||||||
|
item := c.badger.Item()
|
||||||
|
c.k = item.Key()[c.bucket.nameLen:]
|
||||||
|
if c.badgerOpts.PrefetchValues {
|
||||||
|
c.v, c.err = item.ValueCopy(c.v) // bech show: using .ValueCopy on same buffer has same speed as item.Value()
|
||||||
|
}
|
||||||
|
return c.k, c.v, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *badgerCursor) Seek(seek []byte) ([]byte, []byte, error) {
|
||||||
|
select {
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
return nil, nil, c.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
c.initCursor()
|
||||||
|
|
||||||
|
c.bucket.badgerPrefix = append(c.bucket.badgerPrefix[:c.bucket.nameLen], seek...)
|
||||||
|
c.badger.Seek(c.bucket.badgerPrefix)
|
||||||
|
if !c.badger.Valid() {
|
||||||
|
c.k = nil
|
||||||
|
return c.k, c.v, c.err
|
||||||
|
}
|
||||||
|
item := c.badger.Item()
|
||||||
|
c.k = item.Key()[c.bucket.nameLen:]
|
||||||
|
if c.badgerOpts.PrefetchValues {
|
||||||
|
c.v, c.err = item.ValueCopy(c.v)
|
||||||
|
}
|
||||||
|
return c.k, c.v, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *badgerCursor) Next() ([]byte, []byte, error) {
|
||||||
|
select {
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
return nil, nil, c.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
c.badger.Next()
|
||||||
|
if !c.badger.Valid() {
|
||||||
|
c.k = nil
|
||||||
|
return c.k, c.v, c.err
|
||||||
|
}
|
||||||
|
item := c.badger.Item()
|
||||||
|
c.k = item.Key()[c.bucket.nameLen:]
|
||||||
|
if c.badgerOpts.PrefetchValues {
|
||||||
|
c.v, c.err = item.ValueCopy(c.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.k, c.v, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *badgerCursor) Walk(walker func(k, v []byte) (bool, error)) error {
|
||||||
|
for k, v, err := c.First(); k != nil || err != nil; k, v, err = c.Next() {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ok, err := walker(k, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type badgerNoValuesCursor struct {
|
||||||
|
badgerCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *badgerNoValuesCursor) Walk(walker func(k []byte, vSize uint64) (bool, error)) error {
|
||||||
|
for k, vSize, err := c.First(); k != nil || err != nil; k, vSize, err = c.Next() {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ok, err := walker(k, vSize)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *badgerNoValuesCursor) First() ([]byte, uint64, error) {
|
||||||
|
c.initCursor()
|
||||||
|
|
||||||
|
var vSize uint64
|
||||||
|
|
||||||
|
c.badger.Rewind()
|
||||||
|
if !c.badger.Valid() {
|
||||||
|
c.k = nil
|
||||||
|
return c.k, vSize, c.err
|
||||||
|
}
|
||||||
|
item := c.badger.Item()
|
||||||
|
c.k = item.Key()[c.bucket.nameLen:]
|
||||||
|
vSize = uint64(item.ValueSize())
|
||||||
|
return c.k, vSize, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *badgerNoValuesCursor) Seek(seek []byte) ([]byte, uint64, error) {
|
||||||
|
select {
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
return nil, 0, c.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
c.initCursor()
|
||||||
|
|
||||||
|
var vSize uint64
|
||||||
|
|
||||||
|
c.bucket.badgerPrefix = append(c.bucket.badgerPrefix[:c.bucket.nameLen], seek...)
|
||||||
|
c.badger.Seek(c.bucket.badgerPrefix)
|
||||||
|
if !c.badger.Valid() {
|
||||||
|
c.k = nil
|
||||||
|
return c.k, vSize, c.err
|
||||||
|
}
|
||||||
|
item := c.badger.Item()
|
||||||
|
c.k = item.Key()[c.bucket.nameLen:]
|
||||||
|
vSize = uint64(item.ValueSize())
|
||||||
|
|
||||||
|
return c.k, vSize, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *badgerNoValuesCursor) Next() ([]byte, uint64, error) {
|
||||||
|
select {
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
return nil, 0, c.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
var vSize uint64
|
||||||
|
|
||||||
|
c.badger.Next()
|
||||||
|
if !c.badger.Valid() {
|
||||||
|
c.k = nil
|
||||||
|
return c.k, vSize, c.err
|
||||||
|
}
|
||||||
|
item := c.badger.Item()
|
||||||
|
c.k = item.Key()[c.bucket.nameLen:]
|
||||||
|
vSize = uint64(item.ValueSize())
|
||||||
|
|
||||||
|
return c.k, vSize, c.err
|
||||||
|
}
|
260
ethdb/kv_bolt.go
Normal file
260
ethdb/kv_bolt.go
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
package ethdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/ledgerwatch/bolt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BoltKV struct {
|
||||||
|
opts Options
|
||||||
|
bolt *bolt.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes BoltKV
|
||||||
|
// All transactions must be closed before closing the database.
|
||||||
|
func (db *BoltKV) Close() error {
|
||||||
|
return db.bolt.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *BoltKV) Options() Options {
|
||||||
|
return db.opts
|
||||||
|
}
|
||||||
|
|
||||||
|
type boltTx struct {
|
||||||
|
ctx context.Context
|
||||||
|
db *BoltKV
|
||||||
|
|
||||||
|
bolt *bolt.Tx
|
||||||
|
}
|
||||||
|
|
||||||
|
type boltBucket struct {
|
||||||
|
tx *boltTx
|
||||||
|
|
||||||
|
bolt *bolt.Bucket
|
||||||
|
nameLen uint
|
||||||
|
}
|
||||||
|
|
||||||
|
type boltCursor struct {
|
||||||
|
ctx context.Context
|
||||||
|
bucket boltBucket
|
||||||
|
prefix []byte
|
||||||
|
|
||||||
|
bolt *bolt.Cursor
|
||||||
|
|
||||||
|
k []byte
|
||||||
|
v []byte
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *BoltKV) View(ctx context.Context, f func(tx Tx) error) (err error) {
|
||||||
|
t := &boltTx{db: db, ctx: ctx}
|
||||||
|
return db.bolt.View(func(tx *bolt.Tx) error {
|
||||||
|
defer t.cleanup()
|
||||||
|
t.bolt = tx
|
||||||
|
return f(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *BoltKV) Update(ctx context.Context, f func(tx Tx) error) (err error) {
|
||||||
|
t := &boltTx{db: db, ctx: ctx}
|
||||||
|
return db.bolt.Update(func(tx *bolt.Tx) error {
|
||||||
|
defer t.cleanup()
|
||||||
|
t.bolt = tx
|
||||||
|
return f(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *boltTx) Bucket(name []byte) Bucket {
|
||||||
|
b := boltBucket{tx: tx, nameLen: uint(len(name))}
|
||||||
|
b.bolt = tx.bolt.Bucket(name)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *boltTx) cleanup() {
|
||||||
|
// nothing to cleanup
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *boltCursor) Prefix(v []byte) Cursor {
|
||||||
|
c.prefix = v
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *boltCursor) MatchBits(n uint) Cursor {
|
||||||
|
panic("not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *boltCursor) Prefetch(v uint) Cursor {
|
||||||
|
// nothing to do
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *boltCursor) NoValues() NoValuesCursor {
|
||||||
|
return &noValuesBoltCursor{boltCursor: *c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b boltBucket) Get(key []byte) (val []byte, err error) {
|
||||||
|
select {
|
||||||
|
case <-b.tx.ctx.Done():
|
||||||
|
return nil, b.tx.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
val, _ = b.bolt.Get(key)
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b boltBucket) Put(key []byte, value []byte) error {
|
||||||
|
select {
|
||||||
|
case <-b.tx.ctx.Done():
|
||||||
|
return b.tx.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.bolt.Put(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b boltBucket) Delete(key []byte) error {
|
||||||
|
select {
|
||||||
|
case <-b.tx.ctx.Done():
|
||||||
|
return b.tx.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.bolt.Delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b boltBucket) Cursor() Cursor {
|
||||||
|
c := &boltCursor{bucket: b, ctx: b.tx.ctx}
|
||||||
|
// nothing to do
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *boltCursor) initCursor() {
|
||||||
|
if c.bolt != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.bolt = c.bucket.bolt.Cursor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *boltCursor) First() ([]byte, []byte, error) {
|
||||||
|
c.initCursor()
|
||||||
|
|
||||||
|
if c.prefix != nil {
|
||||||
|
c.k, c.v = c.bolt.Seek(c.prefix)
|
||||||
|
} else {
|
||||||
|
c.k, c.v = c.bolt.First()
|
||||||
|
}
|
||||||
|
return c.k, c.v, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *boltCursor) Seek(seek []byte) ([]byte, []byte, error) {
|
||||||
|
select {
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
return nil, nil, c.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
c.initCursor()
|
||||||
|
|
||||||
|
c.k, c.v = c.bolt.Seek(seek)
|
||||||
|
return c.k, c.v, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *boltCursor) Next() ([]byte, []byte, error) {
|
||||||
|
select {
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
return nil, nil, c.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
c.k, c.v = c.bolt.Next()
|
||||||
|
if c.prefix != nil && !bytes.HasPrefix(c.k, c.prefix) {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
return c.k, c.v, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *boltCursor) Walk(walker func(k, v []byte) (bool, error)) error {
|
||||||
|
for k, v, err := c.First(); k != nil || err != nil; k, v, err = c.Next() {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ok, err := walker(k, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type noValuesBoltCursor struct {
|
||||||
|
boltCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *noValuesBoltCursor) Walk(walker func(k []byte, vSize uint64) (bool, error)) error {
|
||||||
|
for k, vSize, err := c.First(); k != nil || err != nil; k, vSize, err = c.Next() {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ok, err := walker(k, vSize)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *noValuesBoltCursor) First() ([]byte, uint64, error) {
|
||||||
|
c.initCursor()
|
||||||
|
|
||||||
|
var vSize uint64
|
||||||
|
var v []byte
|
||||||
|
if c.prefix != nil {
|
||||||
|
c.k, v = c.bolt.Seek(c.prefix)
|
||||||
|
} else {
|
||||||
|
c.k, v = c.bolt.First()
|
||||||
|
}
|
||||||
|
vSize = uint64(len(v))
|
||||||
|
return c.k, vSize, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *noValuesBoltCursor) Seek(seek []byte) ([]byte, uint64, error) {
|
||||||
|
select {
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
return nil, 0, c.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
c.initCursor()
|
||||||
|
|
||||||
|
var vSize uint64
|
||||||
|
var v []byte
|
||||||
|
c.k, v = c.bolt.Seek(seek)
|
||||||
|
vSize = uint64(len(v))
|
||||||
|
return c.k, vSize, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *noValuesBoltCursor) Next() ([]byte, uint64, error) {
|
||||||
|
select {
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
return nil, 0, c.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
var vSize uint64
|
||||||
|
var v []byte
|
||||||
|
c.k, v = c.bolt.Next()
|
||||||
|
vSize = uint64(len(v))
|
||||||
|
if c.prefix != nil && !bytes.HasPrefix(c.k, c.prefix) {
|
||||||
|
return nil, 0, nil
|
||||||
|
}
|
||||||
|
return c.k, vSize, c.err
|
||||||
|
}
|
259
ethdb/kv_remote.go
Normal file
259
ethdb/kv_remote.go
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
package ethdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ledgerwatch/turbo-geth/ethdb/remote"
|
||||||
|
)
|
||||||
|
|
||||||
|
type remoteDB struct {
|
||||||
|
opts Options
|
||||||
|
remote *remote.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *remoteDB) Options() Options {
|
||||||
|
return db.opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes BoltKV
|
||||||
|
// All transactions must be closed before closing the database.
|
||||||
|
func (db *remoteDB) Close() error {
|
||||||
|
return db.remote.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
type remoteTx struct {
|
||||||
|
ctx context.Context
|
||||||
|
db *remoteDB
|
||||||
|
|
||||||
|
remote *remote.Tx
|
||||||
|
}
|
||||||
|
|
||||||
|
type remoteBucket struct {
|
||||||
|
tx *remoteTx
|
||||||
|
|
||||||
|
nameLen uint
|
||||||
|
remote *remote.Bucket
|
||||||
|
}
|
||||||
|
|
||||||
|
type remoteCursor struct {
|
||||||
|
ctx context.Context
|
||||||
|
bucket remoteBucket
|
||||||
|
prefix []byte
|
||||||
|
|
||||||
|
remoteOpts remote.CursorOpts
|
||||||
|
|
||||||
|
remote *remote.Cursor
|
||||||
|
|
||||||
|
k []byte
|
||||||
|
v []byte
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *remoteDB) View(ctx context.Context, f func(tx Tx) error) (err error) {
|
||||||
|
t := &remoteTx{db: db, ctx: ctx}
|
||||||
|
return db.remote.View(ctx, func(tx *remote.Tx) error {
|
||||||
|
t.remote = tx
|
||||||
|
return f(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *remoteDB) Update(ctx context.Context, f func(tx Tx) error) (err error) {
|
||||||
|
return fmt.Errorf("remote db provider doesn't support .Update method")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *remoteTx) Bucket(name []byte) Bucket {
|
||||||
|
b := remoteBucket{tx: tx, nameLen: uint(len(name))}
|
||||||
|
b.remote = tx.remote.Bucket(name)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *remoteTx) cleanup() {
|
||||||
|
// nothing to cleanup
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *remoteCursor) Prefix(v []byte) Cursor {
|
||||||
|
c.prefix = v
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *remoteCursor) MatchBits(n uint) Cursor {
|
||||||
|
panic("not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *remoteCursor) Prefetch(v uint) Cursor {
|
||||||
|
c.remoteOpts.PrefetchSize(uint64(v))
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *remoteCursor) NoValues() NoValuesCursor {
|
||||||
|
c.remoteOpts.PrefetchValues(false)
|
||||||
|
return &remoteNoValuesCursor{remoteCursor: *c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b remoteBucket) Get(key []byte) (val []byte, err error) {
|
||||||
|
select {
|
||||||
|
case <-b.tx.ctx.Done():
|
||||||
|
return nil, b.tx.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err = b.remote.Get(key)
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b remoteBucket) Put(key []byte, value []byte) error {
|
||||||
|
panic("not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b remoteBucket) Delete(key []byte) error {
|
||||||
|
panic("not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b remoteBucket) Cursor() Cursor {
|
||||||
|
c := &remoteCursor{bucket: b, ctx: b.tx.ctx}
|
||||||
|
c.remoteOpts = remote.DefaultCursorOpts
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *remoteCursor) initCursor() {
|
||||||
|
if c.remote != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.remote = c.bucket.remote.Cursor(c.remoteOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *remoteCursor) First() ([]byte, []byte, error) {
|
||||||
|
c.initCursor()
|
||||||
|
|
||||||
|
if c.prefix != nil {
|
||||||
|
c.k, c.v, c.err = c.remote.Seek(c.prefix)
|
||||||
|
} else {
|
||||||
|
c.k, c.v, c.err = c.remote.First()
|
||||||
|
}
|
||||||
|
return c.k, c.v, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *remoteCursor) Seek(seek []byte) ([]byte, []byte, error) {
|
||||||
|
select {
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
return nil, nil, c.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
c.initCursor()
|
||||||
|
|
||||||
|
c.k, c.v, c.err = c.remote.Seek(seek)
|
||||||
|
return c.k, c.v, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *remoteCursor) Next() ([]byte, []byte, error) {
|
||||||
|
select {
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
return nil, nil, c.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
c.k, c.v, c.err = c.remote.Next()
|
||||||
|
if c.err != nil {
|
||||||
|
return nil, nil, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.prefix != nil && !bytes.HasPrefix(c.k, c.prefix) {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
return c.k, c.v, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *remoteCursor) Walk(walker func(k, v []byte) (bool, error)) error {
|
||||||
|
for k, v, err := c.First(); k != nil || err != nil; k, v, err = c.Next() {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ok, err := walker(k, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type remoteNoValuesCursor struct {
|
||||||
|
remoteCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *remoteNoValuesCursor) Walk(walker func(k []byte, vSize uint64) (bool, error)) error {
|
||||||
|
for k, vSize, err := c.First(); k != nil || err != nil; k, vSize, err = c.Next() {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ok, err := walker(k, vSize)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *remoteNoValuesCursor) First() ([]byte, uint64, error) {
|
||||||
|
c.initCursor()
|
||||||
|
|
||||||
|
var vSize uint64
|
||||||
|
var vIsEmpty bool
|
||||||
|
if c.prefix != nil {
|
||||||
|
c.k, vIsEmpty, c.err = c.remote.SeekKey(c.prefix)
|
||||||
|
} else {
|
||||||
|
c.k, vIsEmpty, c.err = c.remote.FirstKey()
|
||||||
|
}
|
||||||
|
if !vIsEmpty {
|
||||||
|
vSize = 1
|
||||||
|
}
|
||||||
|
return c.k, vSize, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *remoteNoValuesCursor) Seek(seek []byte) ([]byte, uint64, error) {
|
||||||
|
select {
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
return nil, 0, c.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
c.initCursor()
|
||||||
|
|
||||||
|
var vSize uint64
|
||||||
|
var vIsEmpty bool
|
||||||
|
c.k, vIsEmpty, c.err = c.remote.SeekKey(seek)
|
||||||
|
if !vIsEmpty {
|
||||||
|
vSize = 1
|
||||||
|
}
|
||||||
|
return c.k, vSize, c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *remoteNoValuesCursor) Next() ([]byte, uint64, error) {
|
||||||
|
select {
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
return nil, 0, c.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
var vSize uint64
|
||||||
|
var vIsEmpty bool
|
||||||
|
c.k, vIsEmpty, c.err = c.remote.NextKey()
|
||||||
|
if !vIsEmpty {
|
||||||
|
vSize = 1
|
||||||
|
}
|
||||||
|
if c.err != nil {
|
||||||
|
return nil, 0, c.err
|
||||||
|
}
|
||||||
|
if c.prefix != nil && !bytes.HasPrefix(c.k, c.prefix) {
|
||||||
|
return nil, 0, nil
|
||||||
|
}
|
||||||
|
return c.k, vSize, c.err
|
||||||
|
}
|
@ -94,7 +94,7 @@ type mutation struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *mutation) KV() *bolt.DB {
|
func (m *mutation) KV() *bolt.DB {
|
||||||
if casted, ok := m.db.(KV); !ok {
|
if casted, ok := m.db.(HasKV); !ok {
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
return casted.KV()
|
return casted.KV()
|
||||||
|
@ -139,7 +139,7 @@ type conn struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DbOpts struct {
|
type DbOpts struct {
|
||||||
dialAddress string
|
DialAddress string
|
||||||
DialFunc DialFunc
|
DialFunc DialFunc
|
||||||
DialTimeout time.Duration
|
DialTimeout time.Duration
|
||||||
PingTimeout time.Duration
|
PingTimeout time.Duration
|
||||||
@ -157,7 +157,7 @@ var DefaultOpts = DbOpts{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (opts DbOpts) Addr(v string) DbOpts {
|
func (opts DbOpts) Addr(v string) DbOpts {
|
||||||
opts.dialAddress = v
|
opts.DialAddress = v
|
||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,10 +261,10 @@ func (closer notifyOnClose) Close() error {
|
|||||||
func Open(parentCtx context.Context, opts DbOpts) (*DB, error) {
|
func Open(parentCtx context.Context, opts DbOpts) (*DB, error) {
|
||||||
if opts.DialFunc == nil {
|
if opts.DialFunc == nil {
|
||||||
opts.DialFunc = func(ctx context.Context) (in io.Reader, out io.Writer, closer io.Closer, err error) {
|
opts.DialFunc = func(ctx context.Context) (in io.Reader, out io.Writer, closer io.Closer, err error) {
|
||||||
if opts.dialAddress == "" {
|
if opts.DialAddress == "" {
|
||||||
return nil, nil, nil, fmt.Errorf("please set opts.dialAddress or opts.DialFunc")
|
return nil, nil, nil, fmt.Errorf("please set opts.DialAddress or opts.DialFunc")
|
||||||
}
|
}
|
||||||
return defaultDialFunc(ctx, opts.dialAddress)
|
return defaultDialFunc(ctx, opts.DialAddress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,10 +343,6 @@ func (db *DB) autoReconnect(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) GetDialAddr() string {
|
|
||||||
return db.opts.dialAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes DB by using the closer field
|
// Close closes DB by using the closer field
|
||||||
func (db *DB) Close() error {
|
func (db *DB) Close() error {
|
||||||
db.cancelConnections()
|
db.cancelConnections()
|
||||||
|
@ -26,7 +26,7 @@ const Version uint64 = 2
|
|||||||
// It runs while the connection is active and keep the entire connection's context
|
// It runs while the connection is active and keep the entire connection's context
|
||||||
// in the local variables
|
// in the local variables
|
||||||
// For tests, bytes.Buffer can be used for both `in` and `out`
|
// For tests, bytes.Buffer can be used for both `in` and `out`
|
||||||
func Server(ctx context.Context, db ethdb.KV, in io.Reader, out io.Writer, closer io.Closer) error {
|
func Server(ctx context.Context, db ethdb.HasKV, in io.Reader, out io.Writer, closer io.Closer) error {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err1 := closer.Close(); err1 != nil {
|
if err1 := closer.Close(); err1 != nil {
|
||||||
logger.Error("Could not close connection", "err", err1)
|
logger.Error("Could not close connection", "err", err1)
|
||||||
@ -475,7 +475,7 @@ func encodeErr(encoder *codec.Encoder, mainError error) {
|
|||||||
var netAddr string
|
var netAddr string
|
||||||
var stopNetInterface context.CancelFunc
|
var stopNetInterface context.CancelFunc
|
||||||
|
|
||||||
func StartDeprecated(db ethdb.KV, addr string) {
|
func StartDeprecated(db ethdb.HasKV, addr string) {
|
||||||
if stopNetInterface != nil {
|
if stopNetInterface != nil {
|
||||||
stopNetInterface()
|
stopNetInterface()
|
||||||
}
|
}
|
||||||
@ -515,7 +515,7 @@ func StartDeprecated(db ethdb.KV, addr string) {
|
|||||||
|
|
||||||
// Listener starts listener that for each incoming connection
|
// Listener starts listener that for each incoming connection
|
||||||
// spawn a go-routine invoking Server
|
// spawn a go-routine invoking Server
|
||||||
func Listen(ctx context.Context, ln net.Listener, db ethdb.KV) {
|
func Listen(ctx context.Context, ln net.Listener, db ethdb.HasKV) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := ln.Close(); err != nil {
|
if err := ln.Close(); err != nil {
|
||||||
logger.Error("Could not close listener", "err", err)
|
logger.Error("Could not close listener", "err", err)
|
||||||
|
@ -184,7 +184,7 @@ func (tr *ResolverStatefulCached) RebuildTrie(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var boltDb *bolt.DB
|
var boltDb *bolt.DB
|
||||||
if hasBolt, ok := db.(ethdb.KV); ok {
|
if hasBolt, ok := db.(ethdb.HasKV); ok {
|
||||||
boltDb = hasBolt.KV()
|
boltDb = hasBolt.KV()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user