github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/syndtr/goleveldb/leveldb/testutil/storage.go (about)

     1  // Copyright (c) 2014, Suryandaru Triandana <syndtr@gmail.com>
     2  // All rights reserved.
     3  //
     4  // Use of this source code is governed by a BSD-style license that can be
     5  // found in the LICENSE file.
     6  
     7  package testutil
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"io"
    13  	"math/rand"
    14  	"os"
    15  	"path/filepath"
    16  	"runtime"
    17  	"strings"
    18  	"sync"
    19  
    20  	. "github.com/insionng/yougam/libraries/onsi/gomega"
    21  
    22  	"github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/storage"
    23  	"github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/util"
    24  )
    25  
    26  var (
    27  	storageMu     sync.Mutex
    28  	storageUseFS  = true
    29  	storageKeepFS = false
    30  	storageNum    int
    31  )
    32  
    33  type StorageMode int
    34  
    35  const (
    36  	ModeOpen StorageMode = 1 << iota
    37  	ModeCreate
    38  	ModeRemove
    39  	ModeRename
    40  	ModeRead
    41  	ModeWrite
    42  	ModeSync
    43  	ModeClose
    44  )
    45  
    46  const (
    47  	modeOpen = iota
    48  	modeCreate
    49  	modeRemove
    50  	modeRename
    51  	modeRead
    52  	modeWrite
    53  	modeSync
    54  	modeClose
    55  
    56  	modeCount
    57  )
    58  
    59  const (
    60  	typeManifest = iota
    61  	typeJournal
    62  	typeTable
    63  	typeTemp
    64  
    65  	typeCount
    66  )
    67  
    68  const flattenCount = modeCount * typeCount
    69  
    70  func flattenType(m StorageMode, t storage.FileType) int {
    71  	var x int
    72  	switch m {
    73  	case ModeOpen:
    74  		x = modeOpen
    75  	case ModeCreate:
    76  		x = modeCreate
    77  	case ModeRemove:
    78  		x = modeRemove
    79  	case ModeRename:
    80  		x = modeRename
    81  	case ModeRead:
    82  		x = modeRead
    83  	case ModeWrite:
    84  		x = modeWrite
    85  	case ModeSync:
    86  		x = modeSync
    87  	case ModeClose:
    88  		x = modeClose
    89  	default:
    90  		panic("invalid storage mode")
    91  	}
    92  	x *= typeCount
    93  	switch t {
    94  	case storage.TypeManifest:
    95  		return x + typeManifest
    96  	case storage.TypeJournal:
    97  		return x + typeJournal
    98  	case storage.TypeTable:
    99  		return x + typeTable
   100  	case storage.TypeTemp:
   101  		return x + typeTemp
   102  	default:
   103  		panic("invalid file type")
   104  	}
   105  }
   106  
   107  func listFlattenType(m StorageMode, t storage.FileType) []int {
   108  	ret := make([]int, 0, flattenCount)
   109  	add := func(x int) {
   110  		x *= typeCount
   111  		switch {
   112  		case t&storage.TypeManifest != 0:
   113  			ret = append(ret, x+typeManifest)
   114  		case t&storage.TypeJournal != 0:
   115  			ret = append(ret, x+typeJournal)
   116  		case t&storage.TypeTable != 0:
   117  			ret = append(ret, x+typeTable)
   118  		case t&storage.TypeTemp != 0:
   119  			ret = append(ret, x+typeTemp)
   120  		}
   121  	}
   122  	switch {
   123  	case m&ModeOpen != 0:
   124  		add(modeOpen)
   125  	case m&ModeCreate != 0:
   126  		add(modeCreate)
   127  	case m&ModeRemove != 0:
   128  		add(modeRemove)
   129  	case m&ModeRename != 0:
   130  		add(modeRename)
   131  	case m&ModeRead != 0:
   132  		add(modeRead)
   133  	case m&ModeWrite != 0:
   134  		add(modeWrite)
   135  	case m&ModeSync != 0:
   136  		add(modeSync)
   137  	case m&ModeClose != 0:
   138  		add(modeClose)
   139  	}
   140  	return ret
   141  }
   142  
   143  func packFile(fd storage.FileDesc) uint64 {
   144  	if fd.Num>>(63-typeCount) != 0 {
   145  		panic("overflow")
   146  	}
   147  	return uint64(fd.Num<<typeCount) | uint64(fd.Type)
   148  }
   149  
   150  func unpackFile(x uint64) storage.FileDesc {
   151  	return storage.FileDesc{storage.FileType(x) & storage.TypeAll, int64(x >> typeCount)}
   152  }
   153  
   154  type emulatedError struct {
   155  	err error
   156  }
   157  
   158  func (err emulatedError) Error() string {
   159  	return fmt.Sprintf("emulated storage error: %v", err.err)
   160  }
   161  
   162  type storageLock struct {
   163  	s *Storage
   164  	r util.Releaser
   165  }
   166  
   167  func (l storageLock) Release() {
   168  	l.r.Release()
   169  	l.s.logI("storage lock released")
   170  }
   171  
   172  type reader struct {
   173  	s  *Storage
   174  	fd storage.FileDesc
   175  	storage.Reader
   176  }
   177  
   178  func (r *reader) Read(p []byte) (n int, err error) {
   179  	err = r.s.emulateError(ModeRead, r.fd.Type)
   180  	if err == nil {
   181  		r.s.stall(ModeRead, r.fd.Type)
   182  		n, err = r.Reader.Read(p)
   183  	}
   184  	r.s.count(ModeRead, r.fd.Type, n)
   185  	if err != nil && err != io.EOF {
   186  		r.s.logI("read error, fd=%s n=%d err=%v", r.fd, n, err)
   187  	}
   188  	return
   189  }
   190  
   191  func (r *reader) ReadAt(p []byte, off int64) (n int, err error) {
   192  	err = r.s.emulateError(ModeRead, r.fd.Type)
   193  	if err == nil {
   194  		r.s.stall(ModeRead, r.fd.Type)
   195  		n, err = r.Reader.ReadAt(p, off)
   196  	}
   197  	r.s.count(ModeRead, r.fd.Type, n)
   198  	if err != nil && err != io.EOF {
   199  		r.s.logI("readAt error, fd=%s offset=%d n=%d err=%v", r.fd, off, n, err)
   200  	}
   201  	return
   202  }
   203  
   204  func (r *reader) Close() (err error) {
   205  	return r.s.fileClose(r.fd, r.Reader)
   206  }
   207  
   208  type writer struct {
   209  	s  *Storage
   210  	fd storage.FileDesc
   211  	storage.Writer
   212  }
   213  
   214  func (w *writer) Write(p []byte) (n int, err error) {
   215  	err = w.s.emulateError(ModeWrite, w.fd.Type)
   216  	if err == nil {
   217  		w.s.stall(ModeWrite, w.fd.Type)
   218  		n, err = w.Writer.Write(p)
   219  	}
   220  	w.s.count(ModeWrite, w.fd.Type, n)
   221  	if err != nil && err != io.EOF {
   222  		w.s.logI("write error, fd=%s n=%d err=%v", w.fd, n, err)
   223  	}
   224  	return
   225  }
   226  
   227  func (w *writer) Sync() (err error) {
   228  	err = w.s.emulateError(ModeSync, w.fd.Type)
   229  	if err == nil {
   230  		w.s.stall(ModeSync, w.fd.Type)
   231  		err = w.Writer.Sync()
   232  	}
   233  	w.s.count(ModeSync, w.fd.Type, 0)
   234  	if err != nil {
   235  		w.s.logI("sync error, fd=%s err=%v", w.fd, err)
   236  	}
   237  	return
   238  }
   239  
   240  func (w *writer) Close() (err error) {
   241  	return w.s.fileClose(w.fd, w.Writer)
   242  }
   243  
   244  type Storage struct {
   245  	storage.Storage
   246  	path    string
   247  	onClose func() (preserve bool, err error)
   248  	onLog   func(str string)
   249  
   250  	lmu sync.Mutex
   251  	lb  bytes.Buffer
   252  
   253  	mu   sync.Mutex
   254  	rand *rand.Rand
   255  	// Open files, true=writer, false=reader
   256  	opens                   map[uint64]bool
   257  	counters                [flattenCount]int
   258  	bytesCounter            [flattenCount]int64
   259  	emulatedError           [flattenCount]error
   260  	emulatedErrorOnce       [flattenCount]bool
   261  	emulatedRandomError     [flattenCount]error
   262  	emulatedRandomErrorProb [flattenCount]float64
   263  	stallCond               sync.Cond
   264  	stalled                 [flattenCount]bool
   265  }
   266  
   267  func (s *Storage) log(skip int, str string) {
   268  	s.lmu.Lock()
   269  	defer s.lmu.Unlock()
   270  	_, file, line, ok := runtime.Caller(skip + 2)
   271  	if ok {
   272  		// Truncate file name at last file name separator.
   273  		if index := strings.LastIndex(file, "/"); index >= 0 {
   274  			file = file[index+1:]
   275  		} else if index = strings.LastIndex(file, "\\"); index >= 0 {
   276  			file = file[index+1:]
   277  		}
   278  	} else {
   279  		file = "???"
   280  		line = 1
   281  	}
   282  	fmt.Fprintf(&s.lb, "%s:%d: ", file, line)
   283  	lines := strings.Split(str, "\n")
   284  	if l := len(lines); l > 1 && lines[l-1] == "" {
   285  		lines = lines[:l-1]
   286  	}
   287  	for i, line := range lines {
   288  		if i > 0 {
   289  			s.lb.WriteString("\n\t")
   290  		}
   291  		s.lb.WriteString(line)
   292  	}
   293  	if s.onLog != nil {
   294  		s.onLog(s.lb.String())
   295  		s.lb.Reset()
   296  	} else {
   297  		s.lb.WriteByte('\n')
   298  	}
   299  }
   300  
   301  func (s *Storage) logISkip(skip int, format string, args ...interface{}) {
   302  	pc, _, _, ok := runtime.Caller(skip + 1)
   303  	if ok {
   304  		if f := runtime.FuncForPC(pc); f != nil {
   305  			fname := f.Name()
   306  			if index := strings.LastIndex(fname, "."); index >= 0 {
   307  				fname = fname[index+1:]
   308  			}
   309  			format = fname + ": " + format
   310  		}
   311  	}
   312  	s.log(skip+1, fmt.Sprintf(format, args...))
   313  }
   314  
   315  func (s *Storage) logI(format string, args ...interface{}) {
   316  	s.logISkip(1, format, args...)
   317  }
   318  
   319  func (s *Storage) OnLog(onLog func(log string)) {
   320  	s.lmu.Lock()
   321  	s.onLog = onLog
   322  	if s.lb.Len() != 0 {
   323  		log := s.lb.String()
   324  		s.onLog(log[:len(log)-1])
   325  		s.lb.Reset()
   326  	}
   327  	s.lmu.Unlock()
   328  }
   329  
   330  func (s *Storage) Log(str string) {
   331  	s.log(1, "Log: "+str)
   332  	s.Storage.Log(str)
   333  }
   334  
   335  func (s *Storage) Lock() (l storage.Lock, err error) {
   336  	l, err = s.Storage.Lock()
   337  	if err != nil {
   338  		s.logI("storage locking failed, err=%v", err)
   339  	} else {
   340  		s.logI("storage locked")
   341  		l = storageLock{s, l}
   342  	}
   343  	return
   344  }
   345  
   346  func (s *Storage) List(t storage.FileType) (fds []storage.FileDesc, err error) {
   347  	fds, err = s.Storage.List(t)
   348  	if err != nil {
   349  		s.logI("list failed, err=%v", err)
   350  		return
   351  	}
   352  	s.logI("list, type=0x%x count=%d", int(t), len(fds))
   353  	return
   354  }
   355  
   356  func (s *Storage) GetMeta() (fd storage.FileDesc, err error) {
   357  	fd, err = s.Storage.GetMeta()
   358  	if err != nil {
   359  		if !os.IsNotExist(err) {
   360  			s.logI("get meta failed, err=%v", err)
   361  		}
   362  		return
   363  	}
   364  	s.logI("get meta, fd=%s", fd)
   365  	return
   366  }
   367  
   368  func (s *Storage) SetMeta(fd storage.FileDesc) error {
   369  	ExpectWithOffset(1, fd.Type).To(Equal(storage.TypeManifest))
   370  	err := s.Storage.SetMeta(fd)
   371  	if err != nil {
   372  		s.logI("set meta failed, fd=%s err=%v", fd, err)
   373  	} else {
   374  		s.logI("set meta, fd=%s", fd)
   375  	}
   376  	return err
   377  }
   378  
   379  func (s *Storage) fileClose(fd storage.FileDesc, closer io.Closer) (err error) {
   380  	err = s.emulateError(ModeClose, fd.Type)
   381  	if err == nil {
   382  		s.stall(ModeClose, fd.Type)
   383  	}
   384  	x := packFile(fd)
   385  	s.mu.Lock()
   386  	defer s.mu.Unlock()
   387  	if err == nil {
   388  		ExpectWithOffset(2, s.opens).To(HaveKey(x), "File closed, fd=%s", fd)
   389  		err = closer.Close()
   390  	}
   391  	s.countNB(ModeClose, fd.Type, 0)
   392  	writer := s.opens[x]
   393  	if err != nil {
   394  		s.logISkip(1, "file close failed, fd=%s writer=%v err=%v", fd, writer, err)
   395  	} else {
   396  		s.logISkip(1, "file closed, fd=%s writer=%v", fd, writer)
   397  		delete(s.opens, x)
   398  	}
   399  	return
   400  }
   401  
   402  func (s *Storage) assertOpen(fd storage.FileDesc) {
   403  	x := packFile(fd)
   404  	ExpectWithOffset(2, s.opens).NotTo(HaveKey(x), "File open, fd=%s writer=%v", fd, s.opens[x])
   405  }
   406  
   407  func (s *Storage) Open(fd storage.FileDesc) (r storage.Reader, err error) {
   408  	err = s.emulateError(ModeOpen, fd.Type)
   409  	if err == nil {
   410  		s.stall(ModeOpen, fd.Type)
   411  	}
   412  	s.mu.Lock()
   413  	defer s.mu.Unlock()
   414  	if err == nil {
   415  		s.assertOpen(fd)
   416  		s.countNB(ModeOpen, fd.Type, 0)
   417  		r, err = s.Storage.Open(fd)
   418  	}
   419  	if err != nil {
   420  		s.logI("file open failed, fd=%s err=%v", fd, err)
   421  	} else {
   422  		s.logI("file opened, fd=%s", fd)
   423  		s.opens[packFile(fd)] = false
   424  		r = &reader{s, fd, r}
   425  	}
   426  	return
   427  }
   428  
   429  func (s *Storage) Create(fd storage.FileDesc) (w storage.Writer, err error) {
   430  	err = s.emulateError(ModeCreate, fd.Type)
   431  	if err == nil {
   432  		s.stall(ModeCreate, fd.Type)
   433  	}
   434  	s.mu.Lock()
   435  	defer s.mu.Unlock()
   436  	if err == nil {
   437  		s.assertOpen(fd)
   438  		s.countNB(ModeCreate, fd.Type, 0)
   439  		w, err = s.Storage.Create(fd)
   440  	}
   441  	if err != nil {
   442  		s.logI("file create failed, fd=%s err=%v", fd, err)
   443  	} else {
   444  		s.logI("file created, fd=%s", fd)
   445  		s.opens[packFile(fd)] = true
   446  		w = &writer{s, fd, w}
   447  	}
   448  	return
   449  }
   450  
   451  func (s *Storage) Remove(fd storage.FileDesc) (err error) {
   452  	err = s.emulateError(ModeRemove, fd.Type)
   453  	if err == nil {
   454  		s.stall(ModeRemove, fd.Type)
   455  	}
   456  	s.mu.Lock()
   457  	defer s.mu.Unlock()
   458  	if err == nil {
   459  		s.assertOpen(fd)
   460  		s.countNB(ModeRemove, fd.Type, 0)
   461  		err = s.Storage.Remove(fd)
   462  	}
   463  	if err != nil {
   464  		s.logI("file remove failed, fd=%s err=%v", fd, err)
   465  	} else {
   466  		s.logI("file removed, fd=%s", fd)
   467  	}
   468  	return
   469  }
   470  
   471  func (s *Storage) ForceRemove(fd storage.FileDesc) (err error) {
   472  	s.countNB(ModeRemove, fd.Type, 0)
   473  	if err = s.Storage.Remove(fd); err != nil {
   474  		s.logI("file remove failed (forced), fd=%s err=%v", fd, err)
   475  	} else {
   476  		s.logI("file removed (forced), fd=%s", fd)
   477  	}
   478  	return
   479  }
   480  
   481  func (s *Storage) Rename(oldfd, newfd storage.FileDesc) (err error) {
   482  	err = s.emulateError(ModeRename, oldfd.Type)
   483  	if err == nil {
   484  		s.stall(ModeRename, oldfd.Type)
   485  	}
   486  	s.mu.Lock()
   487  	defer s.mu.Unlock()
   488  	if err == nil {
   489  		s.assertOpen(oldfd)
   490  		s.assertOpen(newfd)
   491  		s.countNB(ModeRename, oldfd.Type, 0)
   492  		err = s.Storage.Rename(oldfd, newfd)
   493  	}
   494  	if err != nil {
   495  		s.logI("file rename failed, oldfd=%s newfd=%s err=%v", oldfd, newfd, err)
   496  	} else {
   497  		s.logI("file renamed, oldfd=%s newfd=%s", oldfd, newfd)
   498  	}
   499  	return
   500  }
   501  
   502  func (s *Storage) ForceRename(oldfd, newfd storage.FileDesc) (err error) {
   503  	s.countNB(ModeRename, oldfd.Type, 0)
   504  	if err = s.Storage.Rename(oldfd, newfd); err != nil {
   505  		s.logI("file rename failed (forced), oldfd=%s newfd=%s err=%v", oldfd, newfd, err)
   506  	} else {
   507  		s.logI("file renamed (forced), oldfd=%s newfd=%s", oldfd, newfd)
   508  	}
   509  	return
   510  }
   511  
   512  func (s *Storage) openFiles() string {
   513  	out := "Open files:"
   514  	for x, writer := range s.opens {
   515  		fd := unpackFile(x)
   516  		out += fmt.Sprintf("\n ยท fd=%s writer=%v", fd, writer)
   517  	}
   518  	return out
   519  }
   520  
   521  func (s *Storage) CloseCheck() {
   522  	s.mu.Lock()
   523  	defer s.mu.Unlock()
   524  	ExpectWithOffset(1, s.opens).To(BeEmpty(), s.openFiles())
   525  }
   526  
   527  func (s *Storage) OnClose(onClose func() (preserve bool, err error)) {
   528  	s.mu.Lock()
   529  	s.onClose = onClose
   530  	s.mu.Unlock()
   531  }
   532  
   533  func (s *Storage) Close() error {
   534  	s.mu.Lock()
   535  	defer s.mu.Unlock()
   536  	ExpectWithOffset(1, s.opens).To(BeEmpty(), s.openFiles())
   537  	err := s.Storage.Close()
   538  	if err != nil {
   539  		s.logI("storage closing failed, err=%v", err)
   540  	} else {
   541  		s.logI("storage closed")
   542  	}
   543  	var preserve bool
   544  	if s.onClose != nil {
   545  		var err0 error
   546  		if preserve, err0 = s.onClose(); err0 != nil {
   547  			s.logI("onClose error, err=%v", err0)
   548  		}
   549  	}
   550  	if s.path != "" {
   551  		if storageKeepFS || preserve {
   552  			s.logI("storage is preserved, path=%v", s.path)
   553  		} else {
   554  			if err1 := os.RemoveAll(s.path); err1 != nil {
   555  				s.logI("cannot remove storage, err=%v", err1)
   556  			} else {
   557  				s.logI("storage has been removed")
   558  			}
   559  		}
   560  	}
   561  	return err
   562  }
   563  
   564  func (s *Storage) countNB(m StorageMode, t storage.FileType, n int) {
   565  	s.counters[flattenType(m, t)]++
   566  	s.bytesCounter[flattenType(m, t)] += int64(n)
   567  }
   568  
   569  func (s *Storage) count(m StorageMode, t storage.FileType, n int) {
   570  	s.mu.Lock()
   571  	defer s.mu.Unlock()
   572  	s.countNB(m, t, n)
   573  }
   574  
   575  func (s *Storage) ResetCounter(m StorageMode, t storage.FileType) {
   576  	for _, x := range listFlattenType(m, t) {
   577  		s.counters[x] = 0
   578  		s.bytesCounter[x] = 0
   579  	}
   580  }
   581  
   582  func (s *Storage) Counter(m StorageMode, t storage.FileType) (count int, bytes int64) {
   583  	for _, x := range listFlattenType(m, t) {
   584  		count += s.counters[x]
   585  		bytes += s.bytesCounter[x]
   586  	}
   587  	return
   588  }
   589  
   590  func (s *Storage) emulateError(m StorageMode, t storage.FileType) error {
   591  	s.mu.Lock()
   592  	defer s.mu.Unlock()
   593  	x := flattenType(m, t)
   594  	if err := s.emulatedError[x]; err != nil {
   595  		if s.emulatedErrorOnce[x] {
   596  			s.emulatedError[x] = nil
   597  		}
   598  		return emulatedError{err}
   599  	}
   600  	if err := s.emulatedRandomError[x]; err != nil && s.rand.Float64() < s.emulatedRandomErrorProb[x] {
   601  		return emulatedError{err}
   602  	}
   603  	return nil
   604  }
   605  
   606  func (s *Storage) EmulateError(m StorageMode, t storage.FileType, err error) {
   607  	s.mu.Lock()
   608  	defer s.mu.Unlock()
   609  	for _, x := range listFlattenType(m, t) {
   610  		s.emulatedError[x] = err
   611  		s.emulatedErrorOnce[x] = false
   612  	}
   613  }
   614  
   615  func (s *Storage) EmulateErrorOnce(m StorageMode, t storage.FileType, err error) {
   616  	s.mu.Lock()
   617  	defer s.mu.Unlock()
   618  	for _, x := range listFlattenType(m, t) {
   619  		s.emulatedError[x] = err
   620  		s.emulatedErrorOnce[x] = true
   621  	}
   622  }
   623  
   624  func (s *Storage) EmulateRandomError(m StorageMode, t storage.FileType, prob float64, err error) {
   625  	s.mu.Lock()
   626  	defer s.mu.Unlock()
   627  	for _, x := range listFlattenType(m, t) {
   628  		s.emulatedRandomError[x] = err
   629  		s.emulatedRandomErrorProb[x] = prob
   630  	}
   631  }
   632  
   633  func (s *Storage) stall(m StorageMode, t storage.FileType) {
   634  	x := flattenType(m, t)
   635  	s.mu.Lock()
   636  	defer s.mu.Unlock()
   637  	for s.stalled[x] {
   638  		s.stallCond.Wait()
   639  	}
   640  }
   641  
   642  func (s *Storage) Stall(m StorageMode, t storage.FileType) {
   643  	s.mu.Lock()
   644  	defer s.mu.Unlock()
   645  	for _, x := range listFlattenType(m, t) {
   646  		s.stalled[x] = true
   647  	}
   648  }
   649  
   650  func (s *Storage) Release(m StorageMode, t storage.FileType) {
   651  	s.mu.Lock()
   652  	defer s.mu.Unlock()
   653  	for _, x := range listFlattenType(m, t) {
   654  		s.stalled[x] = false
   655  	}
   656  	s.stallCond.Broadcast()
   657  }
   658  
   659  func NewStorage() *Storage {
   660  	var (
   661  		stor storage.Storage
   662  		path string
   663  	)
   664  	if storageUseFS {
   665  		for {
   666  			storageMu.Lock()
   667  			num := storageNum
   668  			storageNum++
   669  			storageMu.Unlock()
   670  			path = filepath.Join(os.TempDir(), fmt.Sprintf("goleveldb-test%d0%d0%d", os.Getuid(), os.Getpid(), num))
   671  			if _, err := os.Stat(path); os.IsNotExist(err) {
   672  				stor, err = storage.OpenFile(path, false)
   673  				ExpectWithOffset(1, err).NotTo(HaveOccurred(), "creating storage at %s", path)
   674  				break
   675  			}
   676  		}
   677  	} else {
   678  		stor = storage.NewMemStorage()
   679  	}
   680  	s := &Storage{
   681  		Storage: stor,
   682  		path:    path,
   683  		rand:    NewRand(),
   684  		opens:   make(map[uint64]bool),
   685  	}
   686  	s.stallCond.L = &s.mu
   687  	if s.path != "" {
   688  		s.logI("using FS storage")
   689  		s.logI("storage path: %s", s.path)
   690  	} else {
   691  		s.logI("using MEM storage")
   692  	}
   693  	return s
   694  }