github.com/moontrade/mdbx-go@v0.4.0/store.go (about) 1 package mdbx 2 3 import ( 4 "os" 5 "path/filepath" 6 "runtime" 7 "sync" 8 "sync/atomic" 9 "time" 10 ) 11 12 const ( 13 DataFileName = "mdbx.dat" 14 LockFileName = "mdbx.lck" 15 ) 16 17 var ( 18 storeMu sync.Mutex 19 stores = make(map[*Store]struct{}) 20 syncer = make(chan struct { 21 s *Store 22 delay time.Duration 23 }, 10) 24 ) 25 26 type Store struct { 27 env *Env 28 path string 29 recovery string 30 recoveryDuration time.Duration 31 closed int64 32 updates uint64 33 synced uint64 34 syncQueued uint64 35 syncPeriod time.Duration 36 writeMu sync.Mutex 37 syncMu sync.Mutex 38 mu sync.Mutex 39 } 40 41 func Open( 42 path string, 43 flags EnvFlags, 44 mode os.FileMode, 45 initEnv func(env *Env, create bool) error, 46 init func(store *Store, create bool) error, 47 ) (*Store, error) { 48 store := &Store{ 49 path: path, 50 } 51 52 env, e := NewEnv() 53 if e != ErrSuccess { 54 return nil, e 55 } 56 store.env = env 57 58 var err error 59 var stat os.FileInfo 60 stat, err = os.Stat(filepath.Join(path, DataFileName)) 61 var create bool 62 if err != nil { 63 create = true 64 } else { 65 // Init 66 create = false 67 } 68 _ = stat 69 70 if initEnv != nil { 71 if err = initEnv(env, create); err != nil && err != ErrSuccess { 72 store.Close() 73 return nil, err 74 } 75 } 76 77 if mode == 0 { 78 mode = 0664 79 } 80 81 if err = store.env.Open(path, flags, mode); err != ErrSuccess { 82 if err == ErrWannaRecovery { 83 start := time.Now() 84 _, output, err := Chk("-v", "-w", store.path) 85 if err != ErrSuccess { 86 _ = store.Close() 87 return nil, err 88 } 89 store.recovery = string(output) 90 store.recoveryDuration = time.Now().Sub(start) 91 92 if err = store.env.Open(path, flags, mode); err != ErrSuccess { 93 _ = store.Close() 94 return nil, err 95 } 96 } else { 97 _ = store.Close() 98 return nil, err 99 } 100 } 101 102 if _, err = store.env.ReaderCheck(); err != nil && err != ErrSuccess { 103 _ = store.Close() 104 return nil, err 105 } 106 107 if init != nil { 108 if err = init(store, create); err != nil && err != ErrSuccess { 109 _ = store.Close() 110 return nil, err 111 } 112 } 113 114 if flags&EnvSafeNoSync != 0 || flags&EnvNoMetaSync != 0 { 115 //syncBytes, _ := env.GetSyncBytes() 116 syncPeriod, _ := env.GetSyncPeriod() 117 118 if syncPeriod > 0 { 119 120 } 121 } 122 123 storeMu.Lock() 124 stores[store] = struct{}{} 125 storeMu.Unlock() 126 return store, nil 127 } 128 129 func (s *Store) Env() *Env { 130 return s.env 131 } 132 133 func (s *Store) IsClosed() bool { 134 s.mu.Lock() 135 defer s.mu.Unlock() 136 return s.closed > 0 137 } 138 139 func (s *Store) Close() error { 140 s.mu.Lock() 141 defer s.mu.Unlock() 142 s.writeMu.Lock() 143 defer s.writeMu.Unlock() 144 145 if s.closed > 0 { 146 return os.ErrClosed 147 } 148 s.closed = time.Now().UnixNano() 149 if s.env != nil { 150 _ = s.env.Close(false) 151 s.env = nil 152 } 153 storeMu.Lock() 154 delete(stores, s) 155 storeMu.Unlock() 156 return nil 157 } 158 159 func (s *Store) Update(fn func(tx *Tx) error) error { 160 return s.UpdateLock(true, fn) 161 } 162 163 func (s *Store) UpdateLock(lockThread bool, fn func(tx *Tx) error) (err error) { 164 if lockThread { 165 // Write transactions must be bound to a single thread. 166 runtime.LockOSThread() 167 defer runtime.UnlockOSThread() 168 } 169 170 // Get exclusive write lock. 171 s.writeMu.Lock() 172 defer s.writeMu.Unlock() 173 tx := Tx{} 174 defer func() { 175 // Abort if panic 176 if !tx.IsCommitted() && !tx.IsAborted() && tx.txn != nil { 177 if e := tx.Abort(); e != ErrSuccess { 178 // Ignore 179 } 180 } 181 182 // Propagate 183 r := recover() 184 if r != nil { 185 if e, ok := r.(error); ok { 186 err = e 187 } 188 } 189 }() 190 191 if err = s.env.Begin(&tx, TxReadWrite); err != ErrSuccess { 192 return err 193 } 194 if err = fn(&tx); err != nil && err != ErrSuccess { 195 // Abort if necessary 196 if !tx.IsAborted() && !tx.IsCommitted() { 197 if e := tx.Abort(); e != ErrSuccess { 198 // Ignore 199 } 200 } 201 return err 202 } else { 203 if !tx.IsCommitted() { 204 if err = tx.Commit(); err != ErrSuccess { 205 return err 206 } 207 } 208 atomic.AddUint64(&s.updates, 1) 209 return nil 210 } 211 } 212 213 func (s *Store) View(fn func(tx *Tx) error) (err error) { 214 tx := Tx{} 215 defer func() { 216 if !tx.IsAborted() { 217 _ = tx.Abort() 218 } 219 // recovery 220 r := recover() 221 if r != nil { 222 if e, ok := r.(error); ok { 223 err = e 224 } 225 } 226 }() 227 if err = s.env.Begin(&tx, TxReadOnly); err != ErrSuccess { 228 return err 229 } 230 return fn(&tx) 231 } 232 233 func (s *Store) ViewRenew(tx *Tx, fn func(tx *Tx) error) (err error) { 234 if tx == nil { 235 return s.View(fn) 236 } 237 defer func() { 238 if !tx.IsReset() { 239 err = tx.Reset() 240 } 241 // recovery 242 r := recover() 243 if r != nil { 244 if e, ok := r.(error); ok { 245 err = e 246 } 247 } 248 }() 249 if err = tx.Renew(); err != ErrSuccess { 250 return err 251 } 252 return fn(tx) 253 } 254 255 func (s *Store) Sync() error { 256 update := atomic.LoadUint64(&s.updates) 257 if err := s.env.Sync(true, false); err != ErrSuccess && err != ErrResultTrue { 258 return err 259 } 260 atomic.StoreUint64(&s.synced, update) 261 return nil 262 } 263 264 func (s *Store) Chk() (result int32, output []byte, err error) { 265 return Chk("-v", filepath.Join(s.path, DataFileName)) 266 } 267 268 func (s *Store) ChkRecover() (result int32, output []byte, err error) { 269 return Chk("-v", "-w", filepath.Join(s.path, DataFileName)) 270 }