github.com/amazechain/amc@v0.1.3/internal/p2p/enode/nodedb.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package enode
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"crypto/rand"
    23  	"encoding/binary"
    24  	"errors"
    25  	"fmt"
    26  	"github.com/amazechain/amc/internal/avm/rlp"
    27  	"github.com/c2h5oh/datasize"
    28  	libcommon "github.com/ledgerwatch/erigon-lib/common"
    29  	"github.com/ledgerwatch/erigon-lib/kv"
    30  	"github.com/ledgerwatch/erigon-lib/kv/mdbx"
    31  	"github.com/ledgerwatch/log/v3"
    32  	"net"
    33  	"os"
    34  	"sync"
    35  	"time"
    36  
    37  	mdbx1 "github.com/erigontech/mdbx-go/mdbx"
    38  	/*
    39  		"github.com/syndtr/goleveldb/leveldb"
    40  		"github.com/syndtr/goleveldb/leveldb/errors"
    41  		"github.com/syndtr/goleveldb/leveldb/iterator"
    42  		"github.com/syndtr/goleveldb/leveldb/opt"
    43  		"github.com/syndtr/goleveldb/leveldb/storage"
    44  		"github.com/syndtr/goleveldb/leveldb/util"
    45  
    46  	*/)
    47  
    48  // Keys in the node database.
    49  const (
    50  	dbVersionKey   = "version" // Version of the database to flush if changes
    51  	dbNodePrefix   = "n:"      // Identifier to prefix node entries with
    52  	dbLocalPrefix  = "local:"
    53  	dbDiscoverRoot = "v4"
    54  	dbDiscv5Root   = "v5"
    55  
    56  	// These fields are stored per ID and IP, the full key is "n:<ID>:v4:<IP>:findfail".
    57  	// Use nodeItemKey to create those keys.
    58  	dbNodeFindFails = "findfail"
    59  	dbNodePing      = "lastping"
    60  	dbNodePong      = "lastpong"
    61  	dbNodeSeq       = "seq"
    62  
    63  	// Local information is keyed by ID only, the full key is "local:<ID>:seq".
    64  	// Use localItemKey to create those keys.
    65  	dbLocalSeq = "seq"
    66  )
    67  
    68  const (
    69  	dbNodeExpiration = 24 * time.Hour // Time after which an unseen node should be dropped.
    70  	dbCleanupCycle   = time.Hour      // Time period for running the expiration task.
    71  	dbVersion        = 9
    72  )
    73  
    74  var (
    75  	errInvalidIP = errors.New("invalid IP")
    76  )
    77  
    78  var zeroIP = make(net.IP, 16)
    79  
    80  // DB is the node database, storing previously seen nodes and any collected metadata about
    81  // them for QoS purposes.
    82  type DB struct {
    83  	kv     kv.RwDB       // Interface to the database itself
    84  	runner sync.Once     // Ensures we can start at most one expirer
    85  	quit   chan struct{} // Channel to signal the expiring thread to stop
    86  }
    87  
    88  // OpenDB opens a node database for storing and retrieving infos about known peers in the
    89  // network. If no path is given an in-memory, temporary database is constructed.
    90  func OpenDB(path string, tmpDir string) (*DB, error) {
    91  	logger := log.New()
    92  	if path == "" {
    93  		return newMemoryDB(logger, tmpDir)
    94  	}
    95  	return newPersistentDB(logger, path)
    96  }
    97  
    98  func bucketsConfig(_ kv.TableCfg) kv.TableCfg {
    99  	return kv.TableCfg{
   100  		kv.Inodes:      {},
   101  		kv.NodeRecords: {},
   102  	}
   103  }
   104  
   105  // newMemoryNodeDB creates a new in-memory node database without a persistent backend.
   106  func newMemoryDB(logger log.Logger, tmpDir string) (*DB, error) {
   107  	db := &DB{quit: make(chan struct{})}
   108  	var err error
   109  	db.kv, err = mdbx.NewMDBX(logger).InMem(tmpDir).Label(kv.SentryDB).WithTableCfg(bucketsConfig).MapSize(1 * datasize.GB).Open()
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	return db, nil
   114  }
   115  
   116  // newPersistentNodeDB creates/opens a persistent node database,
   117  // also flushing its contents in case of a version mismatch.
   118  func newPersistentDB(logger log.Logger, path string) (*DB, error) {
   119  	var db kv.RwDB
   120  	var err error
   121  	db, err = mdbx.NewMDBX(logger).
   122  		Path(path).
   123  		Label(kv.SentryDB).
   124  		WithTableCfg(bucketsConfig).
   125  		MapSize(1024 * datasize.MB).
   126  		GrowthStep(16 * datasize.MB).
   127  		Flags(func(f uint) uint { return f ^ mdbx1.Durable | mdbx1.SafeNoSync }).
   128  		SyncPeriod(2 * time.Second).
   129  		Open()
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	// The nodes contained in the cache correspond to a certain protocol version.
   134  	// Flush all nodes if the version doesn't match.
   135  	currentVer := make([]byte, binary.MaxVarintLen64)
   136  	currentVer = currentVer[:binary.PutVarint(currentVer, int64(dbVersion))]
   137  
   138  	var blob []byte
   139  	if err := db.Update(context.Background(), func(tx kv.RwTx) error {
   140  		c, err := tx.RwCursor(kv.Inodes)
   141  		if err != nil {
   142  			return err
   143  		}
   144  		_, v, errGet := c.SeekExact([]byte(dbVersionKey))
   145  		if errGet != nil {
   146  			return errGet
   147  		}
   148  		if v != nil {
   149  			// v only lives during transaction tx
   150  			blob = make([]byte, len(v))
   151  			copy(blob, v)
   152  			return nil
   153  		}
   154  		return c.Put([]byte(dbVersionKey), currentVer)
   155  	}); err != nil {
   156  		return nil, err
   157  	}
   158  	if blob != nil && !bytes.Equal(blob, currentVer) {
   159  		db.Close()
   160  		if err := os.RemoveAll(path); err != nil {
   161  			return nil, err
   162  		}
   163  		return newPersistentDB(logger, path)
   164  	}
   165  	return &DB{kv: db, quit: make(chan struct{})}, nil
   166  }
   167  
   168  // nodeKey returns the database key for a node record.
   169  func nodeKey(id ID) []byte {
   170  	key := append([]byte(dbNodePrefix), id[:]...)
   171  	key = append(key, ':')
   172  	key = append(key, dbDiscoverRoot...)
   173  	return key
   174  }
   175  
   176  // splitNodeKey returns the node ID of a key created by nodeKey.
   177  func splitNodeKey(key []byte) (id ID, rest []byte) {
   178  	if !bytes.HasPrefix(key, []byte(dbNodePrefix)) {
   179  		return ID{}, nil
   180  	}
   181  	item := key[len(dbNodePrefix):]
   182  	copy(id[:], item[:len(id)])
   183  	return id, item[len(id)+1:]
   184  }
   185  
   186  // nodeItemKey returns the database key for a node metadata field.
   187  func nodeItemKey(id ID, ip net.IP, field string) []byte {
   188  	ip16 := ip.To16()
   189  	if ip16 == nil {
   190  		panic(fmt.Errorf("invalid IP (length %d)", len(ip)))
   191  	}
   192  	return bytes.Join([][]byte{nodeKey(id), ip16, []byte(field)}, []byte{':'})
   193  }
   194  
   195  // splitNodeItemKey returns the components of a key created by nodeItemKey.
   196  func splitNodeItemKey(key []byte) (id ID, ip net.IP, field string) {
   197  	id, key = splitNodeKey(key)
   198  	// Skip discover root.
   199  	if string(key) == dbDiscoverRoot {
   200  		return id, nil, ""
   201  	}
   202  	key = key[len(dbDiscoverRoot)+1:]
   203  	// Split out the IP.
   204  	ip = key[:16]
   205  	if ip4 := ip.To4(); ip4 != nil {
   206  		ip = ip4
   207  	}
   208  	key = key[16+1:]
   209  	// Field is the remainder of key.
   210  	field = string(key)
   211  	return id, ip, field
   212  }
   213  
   214  func v5Key(id ID, ip net.IP, field string) []byte {
   215  	return bytes.Join([][]byte{
   216  		[]byte(dbNodePrefix),
   217  		id[:],
   218  		[]byte(dbDiscv5Root),
   219  		ip.To16(),
   220  		[]byte(field),
   221  	}, []byte{':'})
   222  }
   223  
   224  // localItemKey returns the key of a local node item.
   225  func localItemKey(id ID, field string) []byte {
   226  	key := append([]byte(dbLocalPrefix), id[:]...)
   227  	key = append(key, ':')
   228  	key = append(key, field...)
   229  	return key
   230  }
   231  
   232  func copyBytes(key []byte) (copiedBytes []byte) {
   233  	if key == nil {
   234  		return nil
   235  	}
   236  	copiedBytes = make([]byte, len(key))
   237  	copy(copiedBytes, key)
   238  
   239  	return
   240  }
   241  
   242  // fetchInt64 retrieves an integer associated with a particular key.
   243  func (db *DB) fetchInt64(key []byte) int64 {
   244  	var val int64
   245  	if err := db.kv.View(context.Background(), func(tx kv.Tx) error {
   246  		blob, errGet := tx.GetOne(kv.Inodes, key)
   247  		if errGet != nil {
   248  			return errGet
   249  		}
   250  		if blob != nil {
   251  			if v, read := binary.Varint(blob); read > 0 {
   252  				val = v
   253  			}
   254  		}
   255  		return nil
   256  	}); err != nil {
   257  		return 0
   258  	}
   259  
   260  	return val
   261  }
   262  
   263  // storeInt64 stores an integer in the given key.
   264  func (db *DB) storeInt64(key []byte, n int64) error {
   265  	blob := make([]byte, binary.MaxVarintLen64)
   266  	blob = blob[:binary.PutVarint(blob, n)]
   267  	return db.kv.Update(context.Background(), func(tx kv.RwTx) error {
   268  		return tx.Put(kv.Inodes, copyBytes(key), blob)
   269  	})
   270  }
   271  
   272  // fetchUint64 retrieves an integer associated with a particular key.
   273  func (db *DB) fetchUint64(key []byte) uint64 {
   274  	var val uint64
   275  	if err := db.kv.View(context.Background(), func(tx kv.Tx) error {
   276  		blob, errGet := tx.GetOne(kv.Inodes, key)
   277  		if errGet != nil {
   278  			return errGet
   279  		}
   280  		if blob != nil {
   281  			val, _ = binary.Uvarint(blob)
   282  		}
   283  		return nil
   284  	}); err != nil {
   285  		return 0
   286  	}
   287  	return val
   288  }
   289  
   290  // storeUint64 stores an integer in the given key.
   291  func (db *DB) storeUint64(key []byte, n uint64) error {
   292  	blob := make([]byte, binary.MaxVarintLen64)
   293  	blob = blob[:binary.PutUvarint(blob, n)]
   294  	return db.kv.Update(context.Background(), func(tx kv.RwTx) error {
   295  		return tx.Put(kv.Inodes, copyBytes(key), blob)
   296  	})
   297  }
   298  
   299  // Node retrieves a node with a given id from the database.
   300  func (db *DB) Node(id ID) *Node {
   301  	var blob []byte
   302  	if err := db.kv.View(context.Background(), func(tx kv.Tx) error {
   303  		v, errGet := tx.GetOne(kv.NodeRecords, nodeKey(id))
   304  		if errGet != nil {
   305  			return errGet
   306  		}
   307  		if v != nil {
   308  			blob = make([]byte, len(v))
   309  			copy(blob, v)
   310  		}
   311  		return nil
   312  	}); err != nil {
   313  		return nil
   314  	}
   315  	if blob == nil {
   316  		return nil
   317  	}
   318  	return mustDecodeNode(id[:], blob)
   319  }
   320  
   321  func mustDecodeNode(id, data []byte) *Node {
   322  	node := new(Node)
   323  	if err := rlp.DecodeBytes(data, &node.r); err != nil {
   324  		panic(fmt.Errorf("p2p/enode: can't decode node %x in DB: %w", id, err))
   325  	}
   326  	// Restore node id cache.
   327  	copy(node.id[:], id)
   328  	return node
   329  }
   330  
   331  // UpdateNode inserts - potentially overwriting - a node into the peer database.
   332  func (db *DB) UpdateNode(node *Node) error {
   333  	if node.Seq() < db.NodeSeq(node.ID()) {
   334  		return nil
   335  	}
   336  	blob, err := rlp.EncodeToBytes(&node.r)
   337  	if err != nil {
   338  		return err
   339  	}
   340  	if err := db.kv.Update(context.Background(), func(tx kv.RwTx) error {
   341  		return tx.Put(kv.NodeRecords, nodeKey(node.ID()), blob)
   342  	}); err != nil {
   343  		return err
   344  	}
   345  	return db.storeUint64(nodeItemKey(node.ID(), zeroIP, dbNodeSeq), node.Seq())
   346  }
   347  
   348  // NodeSeq returns the stored record sequence number of the given node.
   349  func (db *DB) NodeSeq(id ID) uint64 {
   350  	return db.fetchUint64(nodeItemKey(id, zeroIP, dbNodeSeq))
   351  }
   352  
   353  // Resolve returns the stored record of the node if it has a larger sequence
   354  // number than n.
   355  func (db *DB) Resolve(n *Node) *Node {
   356  	if n.Seq() > db.NodeSeq(n.ID()) {
   357  		return n
   358  	}
   359  	return db.Node(n.ID())
   360  }
   361  
   362  // DeleteNode deletes all information associated with a node.
   363  func (db *DB) DeleteNode(id ID) {
   364  	deleteRange(db.kv, nodeKey(id))
   365  }
   366  
   367  func deleteRange(db kv.RwDB, prefix []byte) {
   368  	if err := db.Update(context.Background(), func(tx kv.RwTx) error {
   369  		for bucket := range bucketsConfig(nil) {
   370  			if err := deleteRangeInBucket(tx, prefix, bucket); err != nil {
   371  				return err
   372  			}
   373  		}
   374  		return nil
   375  	}); err != nil {
   376  		log.Warn("nodeDB.deleteRange failed", "err", err)
   377  	}
   378  }
   379  
   380  func deleteRangeInBucket(tx kv.RwTx, prefix []byte, bucket string) error {
   381  	c, err := tx.RwCursor(bucket)
   382  	if err != nil {
   383  		return err
   384  	}
   385  	var k []byte
   386  	for k, _, err = c.Seek(prefix); (err == nil) && (k != nil) && bytes.HasPrefix(k, prefix); k, _, err = c.Next() {
   387  		if err = c.DeleteCurrent(); err != nil {
   388  			break
   389  		}
   390  	}
   391  	return err
   392  }
   393  
   394  // ensureExpirer is a small helper method ensuring that the data expiration
   395  // mechanism is running. If the expiration goroutine is already running, this
   396  // method simply returns.
   397  //
   398  // The goal is to start the data evacuation only after the network successfully
   399  // bootstrapped itself (to prevent dumping potentially useful seed nodes). Since
   400  // it would require significant overhead to exactly trace the first successful
   401  // convergence, it's simpler to "ensure" the correct state when an appropriate
   402  // condition occurs (i.e. a successful bonding), and discard further events.
   403  func (db *DB) ensureExpirer() {
   404  	db.runner.Do(func() { go db.expirer() })
   405  }
   406  
   407  // expirer should be started in a go routine, and is responsible for looping ad
   408  // infinitum and dropping stale data from the database.
   409  func (db *DB) expirer() {
   410  	tick := time.NewTicker(dbCleanupCycle)
   411  	defer tick.Stop()
   412  	for {
   413  		select {
   414  		case <-tick.C:
   415  			db.expireNodes()
   416  		case <-db.quit:
   417  			return
   418  		}
   419  	}
   420  }
   421  
   422  // expireNodes iterates over the database and deletes all nodes that have not
   423  // been seen (i.e. received a pong from) for some time.
   424  func (db *DB) expireNodes() {
   425  	var (
   426  		threshold    = time.Now().Add(-dbNodeExpiration).Unix()
   427  		youngestPong int64
   428  	)
   429  	var toDelete [][]byte
   430  	if err := db.kv.View(context.Background(), func(tx kv.Tx) error {
   431  		c, err := tx.Cursor(kv.Inodes)
   432  		if err != nil {
   433  			return err
   434  		}
   435  		p := []byte(dbNodePrefix)
   436  		var prevId ID
   437  		var empty = true
   438  		for k, v, err := c.Seek(p); bytes.HasPrefix(k, p); k, v, err = c.Next() {
   439  			if err != nil {
   440  				return err
   441  			}
   442  			id, ip, field := splitNodeItemKey(k)
   443  			if field == dbNodePong {
   444  				time, _ := binary.Varint(v)
   445  				if time > youngestPong {
   446  					youngestPong = time
   447  				}
   448  				if time < threshold {
   449  					// Last pong from this IP older than threshold, remove fields belonging to it.
   450  					toDelete = append(toDelete, nodeItemKey(id, ip, ""))
   451  				}
   452  			}
   453  			if id != prevId {
   454  				if youngestPong > 0 && youngestPong < threshold {
   455  					toDelete = append(toDelete, nodeKey(prevId))
   456  				}
   457  				youngestPong = 0
   458  			}
   459  			prevId = id
   460  			empty = false
   461  		}
   462  		if !empty {
   463  			if youngestPong > 0 && youngestPong < threshold {
   464  				toDelete = append(toDelete, nodeKey(prevId))
   465  			}
   466  			youngestPong = 0
   467  		}
   468  		return nil
   469  	}); err != nil {
   470  		log.Warn("nodeDB.expireNodes failed", "err", err)
   471  	}
   472  	for _, td := range toDelete {
   473  		deleteRange(db.kv, td)
   474  	}
   475  }
   476  
   477  // LastPingReceived retrieves the time of the last ping packet received from
   478  // a remote node.
   479  func (db *DB) LastPingReceived(id ID, ip net.IP) time.Time {
   480  	if ip = ip.To16(); ip == nil {
   481  		return time.Time{}
   482  	}
   483  	return time.Unix(db.fetchInt64(nodeItemKey(id, ip, dbNodePing)), 0)
   484  }
   485  
   486  // UpdateLastPingReceived updates the last time we tried contacting a remote node.
   487  func (db *DB) UpdateLastPingReceived(id ID, ip net.IP, instance time.Time) error {
   488  	if ip = ip.To16(); ip == nil {
   489  		return errInvalidIP
   490  	}
   491  	return db.storeInt64(nodeItemKey(id, ip, dbNodePing), instance.Unix())
   492  }
   493  
   494  // LastPongReceived retrieves the time of the last successful pong from remote node.
   495  func (db *DB) LastPongReceived(id ID, ip net.IP) time.Time {
   496  	if ip = ip.To16(); ip == nil {
   497  		return time.Time{}
   498  	}
   499  	// Launch expirer
   500  	db.ensureExpirer()
   501  	return time.Unix(db.fetchInt64(nodeItemKey(id, ip, dbNodePong)), 0)
   502  }
   503  
   504  // UpdateLastPongReceived updates the last pong time of a node.
   505  func (db *DB) UpdateLastPongReceived(id ID, ip net.IP, instance time.Time) error {
   506  	if ip = ip.To16(); ip == nil {
   507  		return errInvalidIP
   508  	}
   509  	return db.storeInt64(nodeItemKey(id, ip, dbNodePong), instance.Unix())
   510  }
   511  
   512  // FindFails retrieves the number of findnode failures since bonding.
   513  func (db *DB) FindFails(id ID, ip net.IP) int {
   514  	if ip = ip.To16(); ip == nil {
   515  		return 0
   516  	}
   517  	return int(db.fetchInt64(nodeItemKey(id, ip, dbNodeFindFails)))
   518  }
   519  
   520  // UpdateFindFails updates the number of findnode failures since bonding.
   521  func (db *DB) UpdateFindFails(id ID, ip net.IP, fails int) error {
   522  	if ip = ip.To16(); ip == nil {
   523  		return errInvalidIP
   524  	}
   525  	return db.storeInt64(nodeItemKey(id, ip, dbNodeFindFails), int64(fails))
   526  }
   527  
   528  // FindFailsV5 retrieves the discv5 findnode failure counter.
   529  func (db *DB) FindFailsV5(id ID, ip net.IP) int {
   530  	if ip = ip.To16(); ip == nil {
   531  		return 0
   532  	}
   533  	return int(db.fetchInt64(v5Key(id, ip, dbNodeFindFails)))
   534  }
   535  
   536  // UpdateFindFailsV5 stores the discv5 findnode failure counter.
   537  func (db *DB) UpdateFindFailsV5(id ID, ip net.IP, fails int) error {
   538  	if ip = ip.To16(); ip == nil {
   539  		return errInvalidIP
   540  	}
   541  	return db.storeInt64(v5Key(id, ip, dbNodeFindFails), int64(fails))
   542  }
   543  
   544  // LocalSeq retrieves the local record sequence counter.
   545  func (db *DB) localSeq(id ID) uint64 {
   546  	return db.fetchUint64(localItemKey(id, dbLocalSeq))
   547  }
   548  
   549  // storeLocalSeq stores the local record sequence counter.
   550  func (db *DB) storeLocalSeq(id ID, n uint64) {
   551  	db.storeUint64(localItemKey(id, dbLocalSeq), n)
   552  }
   553  
   554  // QuerySeeds retrieves random nodes to be used as potential seed nodes
   555  // for bootstrapping.
   556  func (db *DB) QuerySeeds(n int, maxAge time.Duration) []*Node {
   557  	var (
   558  		now   = time.Now()
   559  		nodes = make([]*Node, 0, n)
   560  		id    ID
   561  	)
   562  
   563  	if err := db.kv.View(context.Background(), func(tx kv.Tx) error {
   564  		c, err := tx.Cursor(kv.NodeRecords)
   565  		if err != nil {
   566  			return err
   567  		}
   568  	seek:
   569  		for seeks := 0; len(nodes) < n && seeks < n*5; seeks++ {
   570  			// Seek to a random entry. The first byte is incremented by a
   571  			// random amount each time in order to increase the likelihood
   572  			// of hitting all existing nodes in very small databases.
   573  			ctr := id[0]
   574  			rand.Read(id[:])
   575  			id[0] = ctr + id[0]%16
   576  			var n *Node
   577  			for k, v, err := c.Seek(nodeKey(id)); k != nil && n == nil; k, v, err = c.Next() {
   578  				if err != nil {
   579  					return err
   580  				}
   581  				id, rest := splitNodeKey(k)
   582  				if string(rest) == dbDiscoverRoot {
   583  					n = mustDecodeNode(id[:], v)
   584  				}
   585  			}
   586  			if n == nil {
   587  				id[0] = 0
   588  				continue // iterator exhausted
   589  			}
   590  			db.ensureExpirer()
   591  			pongKey := nodeItemKey(n.ID(), n.IP(), dbNodePong)
   592  			var lastPongReceived int64
   593  			blob, errGet := tx.GetOne(kv.Inodes, pongKey)
   594  			if errGet != nil {
   595  				return errGet
   596  			}
   597  			if blob != nil {
   598  				if v, read := binary.Varint(blob); read > 0 {
   599  					lastPongReceived = v
   600  				}
   601  			}
   602  			if now.Sub(time.Unix(lastPongReceived, 0)) > maxAge {
   603  				continue
   604  			}
   605  			for i := range nodes {
   606  				if nodes[i].ID() == n.ID() {
   607  					continue seek // duplicate
   608  				}
   609  			}
   610  			nodes = append(nodes, n)
   611  		}
   612  		return nil
   613  	}); err != nil {
   614  		log.Warn("nodeDB.QuerySeeds failed", "err", err)
   615  	}
   616  	return nodes
   617  }
   618  
   619  // close flushes and closes the database files.
   620  func (db *DB) Close() {
   621  	select {
   622  	case <-db.quit:
   623  		return // means closed already
   624  	default:
   625  	}
   626  	if db.quit == nil {
   627  		return
   628  	}
   629  	libcommon.SafeClose(db.quit)
   630  	db.kv.Close()
   631  }