github.com/amazechain/amc@v0.1.3/internal/amcdb/lmdb/lmdb.go (about)

     1  // Copyright 2022 The AmazeChain Authors
     2  // This file is part of the AmazeChain library.
     3  //
     4  // The AmazeChain 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 AmazeChain 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 AmazeChain library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package lmdb
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"os"
    23  	"strings"
    24  	"sync"
    25  
    26  	"github.com/amazechain/amc/common/db"
    27  	"github.com/amazechain/amc/conf"
    28  	"github.com/amazechain/amc/log"
    29  	"github.com/amazechain/amc/utils"
    30  	"github.com/c2h5oh/datasize"
    31  	"github.com/erigontech/mdbx-go/mdbx"
    32  )
    33  
    34  var (
    35  	_lmdb Lmdb
    36  )
    37  
    38  type Lmdb struct {
    39  	*mdbx.Env
    40  	config *conf.DatabaseConfig
    41  
    42  	ctx    context.Context
    43  	cancel context.CancelFunc
    44  
    45  	once    sync.Once
    46  	running bool
    47  	wg      sync.WaitGroup
    48  	mu      sync.RWMutex
    49  
    50  	mDBI map[string]*DBI
    51  }
    52  
    53  func NewLMDB(c context.Context, nodeConfig *conf.NodeConfig, config *conf.DatabaseConfig) (*Lmdb, error) { //ethdb.Database
    54  	if _lmdb.running {
    55  		return &_lmdb, nil
    56  	}
    57  	env, err := mdbx.NewEnv()
    58  	if err != nil {
    59  		log.Errorf("failed to create lmdb, err %v", err)
    60  	}
    61  
    62  	if config.Debug {
    63  		if err := env.SetDebug(mdbx.LogLvlDebug, mdbx.DbgDoNotChange, mdbx.LoggerDoNotChange); err != nil {
    64  			log.Errorf("failed to set lmdb with deubg, err: %v", err)
    65  			return nil, err
    66  		}
    67  	}
    68  
    69  	if err = env.SetOption(mdbx.OptMaxDB, config.MaxDB); err != nil {
    70  		log.Errorf("failed to set max db, err: %v", err)
    71  		return nil, err
    72  	}
    73  
    74  	if err = env.SetOption(mdbx.OptMaxReaders, config.MaxReaders); err != nil {
    75  		log.Errorf("failed to set max reader, err: %v", err)
    76  		return nil, err
    77  	}
    78  
    79  	if err = env.SetGeometry(-1, -1, int(3*datasize.TB), int(2*datasize.GB), -1, 4*1024); err != nil {
    80  		log.Errorf("failed to set geometry, err: %v", err)
    81  		return nil, err
    82  	}
    83  	var file string
    84  	//todo how deal with windows?
    85  	if strings.HasSuffix(config.DBPath, "/") {
    86  		file = fmt.Sprintf("%s/%s%s", nodeConfig.DataDir, config.DBPath, config.DBName)
    87  	} else {
    88  		file = fmt.Sprintf("%s/%s/%s", nodeConfig.DataDir, config.DBPath, config.DBName)
    89  	}
    90  
    91  	if !utils.Exists(file) {
    92  		if err := utils.MkdirAll(file, os.ModePerm); err != nil {
    93  			return nil, err
    94  		}
    95  	}
    96  
    97  	if err := env.Open(file, 0, os.ModePerm); err != nil {
    98  		if mdbx.IsNotExist(err) {
    99  			log.Warnf("failed to open db %s, path not exist, err: %v", file, err)
   100  			if err := utils.MkdirAll(file, 0666); err != nil {
   101  				return nil, err
   102  			}
   103  		} else {
   104  			log.Errorf("failed to open db %s, err: %v", file, err)
   105  			return nil, err
   106  		}
   107  	}
   108  
   109  	ctx, cancel := context.WithCancel(c)
   110  	_lmdb = Lmdb{
   111  		Env:     env,
   112  		config:  config,
   113  		ctx:     ctx,
   114  		cancel:  cancel,
   115  		running: true,
   116  		mDBI:    make(map[string]*DBI),
   117  	}
   118  
   119  	return &_lmdb, nil
   120  }
   121  
   122  func (m *Lmdb) OpenReader(dbName string) (reader db.IDatabaseReader, err error) {
   123  	return m.openDBI(dbName)
   124  }
   125  
   126  func (m *Lmdb) OpenWriter(dbName string) (writer db.IDatabaseWriter, err error) {
   127  	return m.openDBI(dbName)
   128  }
   129  
   130  func (m *Lmdb) Open(dbName string) (rw db.IDatabaseWriterReader, err error) {
   131  	return m.openDBI(dbName)
   132  }
   133  
   134  /*
   135  Snapshot:
   136  */
   137  func (m *Lmdb) Snapshot() (db.ISnapshot, error) {
   138  	return newSnapshot(m.ctx, nil, m.Env)
   139  }
   140  
   141  func (m *Lmdb) openDBI(dbName string) (rw db.IDatabaseWriterReader, err error) {
   142  	m.mu.RLock()
   143  	if dbi, ok := m.mDBI[dbName]; ok {
   144  		m.mu.RUnlock()
   145  		return dbi, nil
   146  	}
   147  	m.mu.RUnlock()
   148  
   149  	m.mu.Lock()
   150  	defer m.mu.Unlock()
   151  	dbi, err := newDBI(m.ctx, m.Env, dbName)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	m.mDBI[dbName] = dbi
   157  	return dbi, nil
   158  }
   159  
   160  func (m *Lmdb) Close() (err error) {
   161  	m.once.Do(func() {
   162  		m.running = false
   163  		m.cancel()
   164  		m.Env.Close()
   165  	})
   166  	return
   167  }