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  }