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 }