github.com/balzaczyy/golucene@v0.0.0-20151210033525-d0be9ee89713/core/store/directory.go (about)

     1  package store
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"github.com/balzaczyy/golucene/core/util"
     7  	"io"
     8  	"time"
     9  )
    10  
    11  // store/IOContext.java
    12  
    13  const (
    14  	IO_CONTEXT_TYPE_MERGE   = 1
    15  	IO_CONTEXT_TYPE_READ    = 2
    16  	IO_CONTEXT_TYPE_FLUSH   = 3
    17  	IO_CONTEXT_TYPE_DEFAULT = 4
    18  )
    19  
    20  type IOContextType int
    21  
    22  var (
    23  	IO_CONTEXT_DEFAULT  = NewIOContextFromType(IOContextType(IO_CONTEXT_TYPE_DEFAULT))
    24  	IO_CONTEXT_READONCE = NewIOContextBool(true)
    25  	IO_CONTEXT_READ     = NewIOContextBool(false)
    26  )
    27  
    28  /*
    29  IOContext holds additional details on the merge/search context. A
    30  IOContext object can never be initialized as nil as passed as a
    31  parameter to either OpenInput() or CreateOutput()
    32  */
    33  type IOContext struct {
    34  	context   IOContextType
    35  	MergeInfo *MergeInfo
    36  	FlushInfo *FlushInfo
    37  	readOnce  bool
    38  }
    39  
    40  func NewIOContextForFlush(flushInfo *FlushInfo) IOContext {
    41  	assert(flushInfo != nil)
    42  	return IOContext{
    43  		context:   IOContextType(IO_CONTEXT_TYPE_FLUSH),
    44  		readOnce:  false,
    45  		FlushInfo: flushInfo,
    46  	}
    47  }
    48  
    49  func NewIOContextFromType(context IOContextType) IOContext {
    50  	assert2(context != IO_CONTEXT_TYPE_MERGE, "Use NewIOContextForMerge() to create a MERGE IOContext")
    51  	assert2(context != IO_CONTEXT_TYPE_FLUSH, "Use NewIOContextForFlush() to create a FLUSH IOContext")
    52  	return IOContext{
    53  		context:  context,
    54  		readOnce: false,
    55  	}
    56  }
    57  
    58  func NewIOContextBool(readOnce bool) IOContext {
    59  	return IOContext{
    60  		context:  IOContextType(IO_CONTEXT_TYPE_READ),
    61  		readOnce: readOnce,
    62  	}
    63  }
    64  
    65  func NewIOContextForMerge(mergeInfo *MergeInfo) IOContext {
    66  	assert2(mergeInfo != nil, "MergeInfo must not be nil if context is MERGE")
    67  	return IOContext{
    68  		context:   IOContextType(IO_CONTEXT_TYPE_MERGE),
    69  		MergeInfo: mergeInfo,
    70  		readOnce:  false,
    71  	}
    72  }
    73  
    74  func (ctx IOContext) String() string {
    75  	return fmt.Sprintf("IOContext [context=%v, mergeInfo=%v, flushInfo=%v, readOnce=%v",
    76  		ctx.context, ctx.MergeInfo, ctx.FlushInfo, ctx.readOnce)
    77  }
    78  
    79  type FlushInfo struct {
    80  	NumDocs              int
    81  	EstimatedSegmentSize int64
    82  }
    83  
    84  type MergeInfo struct {
    85  	TotalDocCount       int
    86  	EstimatedMergeBytes int64
    87  	IsExternal          bool
    88  	MergeMaxNumSegments int
    89  }
    90  
    91  // store/Lock.java
    92  
    93  // How long obtain() waits, in milliseconds,
    94  // in between attempts to acquire the lock.
    95  const LOCK_POOL_INTERVAL = 1000
    96  
    97  // Pass this value to obtain() to try
    98  // forever to obtain the lock
    99  const LOCK_OBTAIN_WAIT_FOREVER = -1
   100  
   101  /*
   102  An interprocess mutex lock.
   103  
   104  Typical use might look like:
   105  
   106  	WithLock(directory.MakeLock("my.lock"), func() interface{} {
   107  		// code to execute while locked
   108  	})
   109  */
   110  type Lock interface {
   111  	// Releases exclusive access.
   112  	io.Closer
   113  	// Attempts to obtain exclusive access and immediately return
   114  	// upon success or failure. Use Close() to release the lock.
   115  	Obtain() (ok bool, err error)
   116  	// Attempts to obtain an exclusive lock within amount of time
   117  	// given. Pools once per LOCK_POLL_INTERVAL (currently 1000)
   118  	// milliseconds until lockWaitTimeout is passed.
   119  	ObtainWithin(lockWaitTimeout int64) (ok bool, err error)
   120  	// Returns true if the resource is currently locked. Note that one
   121  	// must still call obtain() before using the resource.
   122  	IsLocked() bool
   123  }
   124  
   125  type LockImpl struct {
   126  	self Lock
   127  	// If a lock obtain called, this failureReason may be set with the
   128  	// "root cause" error as to why the lock was not obtained
   129  	failureReason error
   130  }
   131  
   132  func NewLockImpl(self Lock) *LockImpl {
   133  	return &LockImpl{self: self}
   134  }
   135  
   136  func (lock *LockImpl) ObtainWithin(lockWaitTimeout int64) (locked bool, err error) {
   137  	lock.failureReason = nil
   138  	locked, err = lock.self.Obtain()
   139  	if err != nil {
   140  		return
   141  	}
   142  	assert2(lockWaitTimeout >= 0 || lockWaitTimeout == LOCK_OBTAIN_WAIT_FOREVER,
   143  		"lockWaitTimeout should be LOCK_OBTAIN_WAIT_FOREVER or a non-negative number (got %v)",
   144  		lockWaitTimeout)
   145  
   146  	maxSleepCount := lockWaitTimeout / LOCK_POOL_INTERVAL
   147  	for sleepCount := int64(0); !locked; locked, err = lock.self.Obtain() {
   148  		if lockWaitTimeout != LOCK_OBTAIN_WAIT_FOREVER && sleepCount >= maxSleepCount {
   149  			reason := fmt.Sprintf("Lock obtain time out: %v", lock)
   150  			if lock.failureReason != nil {
   151  				reason = fmt.Sprintf("%v: %v", reason, lock.failureReason)
   152  			}
   153  			err = errors.New(reason)
   154  			return
   155  		}
   156  		sleepCount++
   157  		time.Sleep(LOCK_POOL_INTERVAL * time.Millisecond)
   158  	}
   159  	return
   160  }
   161  
   162  // Utility to execute code with exclusive access.
   163  func WithLock(lock Lock, lockWaitTimeout int64, body func() interface{}) interface{} {
   164  	panic("not implemeted yet")
   165  }
   166  
   167  type LockFactory interface {
   168  	Make(name string) Lock
   169  	Clear(name string) error
   170  	SetLockPrefix(prefix string)
   171  	LockPrefix() string
   172  }
   173  
   174  type LockFactoryImpl struct {
   175  	lockPrefix string
   176  }
   177  
   178  func (f *LockFactoryImpl) SetLockPrefix(prefix string) {
   179  	f.lockPrefix = prefix
   180  }
   181  
   182  func (f *LockFactoryImpl) LockPrefix() string {
   183  	return f.lockPrefix
   184  }
   185  
   186  type FSLockFactory struct {
   187  	*LockFactoryImpl
   188  	lockDir string // can not be set twice
   189  }
   190  
   191  func newFSLockFactory() *FSLockFactory {
   192  	ans := &FSLockFactory{}
   193  	ans.LockFactoryImpl = &LockFactoryImpl{}
   194  	return ans
   195  }
   196  
   197  func (f *FSLockFactory) setLockDir(lockDir string) {
   198  	if f.lockDir != "" {
   199  		panic("You can set the lock directory for this factory only once.")
   200  	}
   201  	f.lockDir = lockDir
   202  }
   203  
   204  func (f *FSLockFactory) getLockDir() string {
   205  	return f.lockDir
   206  }
   207  
   208  func (f *FSLockFactory) Clear(name string) error {
   209  	panic("invalid")
   210  }
   211  
   212  func (f *FSLockFactory) Make(name string) Lock {
   213  	panic("invalid")
   214  }
   215  
   216  func (f *FSLockFactory) String() string {
   217  	return fmt.Sprintf("FSLockFactory@%v", f.lockDir)
   218  }
   219  
   220  type Directory interface {
   221  	io.Closer
   222  	// Files related methods
   223  	ListAll() (paths []string, err error)
   224  	// Returns true iff a file with the given name exists.
   225  	// @deprecated This method will be removed in 5.0
   226  	FileExists(name string) bool
   227  	// Removes an existing file in the directory.
   228  	DeleteFile(name string) error
   229  	// Returns the length of a file in the directory. This method
   230  	// follows the following contract:
   231  	// 	- Must return error if the file doesn't exists.
   232  	// 	- Returns a value >=0 if the file exists, which specifies its
   233  	// length.
   234  	FileLength(name string) (n int64, err error)
   235  	// Creates a new, empty file in the directory with the given name.
   236  	// Returns a stream writing this file.
   237  	CreateOutput(name string, ctx IOContext) (out IndexOutput, err error)
   238  	// Ensure that any writes to these files ar emoved to stable
   239  	// storage. Lucene uses this to properly commit changes to the
   240  	// index, to prevent a machine/OS crash from corrupting the index.
   241  	//
   242  	// NOTE: Clients may call this method for same files over and over
   243  	// again, so some impls might optimize for that. For other impls
   244  	// the operation can be a noop, for various reasons.
   245  	Sync(names []string) error
   246  	OpenInput(name string, context IOContext) (in IndexInput, err error)
   247  	// Returns a stream reading an existing file, computing checksum as it reads
   248  	OpenChecksumInput(name string, ctx IOContext) (ChecksumIndexInput, error)
   249  	// Locks related methods
   250  	MakeLock(name string) Lock
   251  	ClearLock(name string) error
   252  	SetLockFactory(lockFactory LockFactory)
   253  	LockFactory() LockFactory
   254  	LockID() string
   255  	// Utilities
   256  	Copy(to Directory, src, dest string, ctx IOContext) error
   257  
   258  	EnsureOpen()
   259  }
   260  
   261  type DirectoryImplSPI interface {
   262  	OpenInput(string, IOContext) (IndexInput, error)
   263  	LockFactory() LockFactory
   264  }
   265  
   266  type DirectoryImpl struct {
   267  	spi DirectoryImplSPI
   268  }
   269  
   270  func NewDirectoryImpl(spi DirectoryImplSPI) *DirectoryImpl {
   271  	return &DirectoryImpl{spi}
   272  }
   273  
   274  func (d *DirectoryImpl) OpenChecksumInput(name string, ctx IOContext) (ChecksumIndexInput, error) {
   275  	in, err := d.spi.OpenInput(name, ctx)
   276  	if err != nil {
   277  		return nil, err
   278  	}
   279  	return newBufferedChecksumIndexInput(in), nil
   280  }
   281  
   282  /*
   283  Return a string identifier that uniquely differentiates
   284  this Directory instance from other Directory instances.
   285  This ID should be the same if two Directory instances
   286  (even in different JVMs and/or on different machines)
   287  are considered "the same index".  This is how locking
   288  "scopes" to the right index.
   289  */
   290  func (d *DirectoryImpl) LockID() string {
   291  	return fmt.Sprintf("%v", d)
   292  }
   293  
   294  func (d *DirectoryImpl) String() string {
   295  	return fmt.Sprintf("@hex lockFactory=%v", d.spi.LockFactory)
   296  }
   297  
   298  /*
   299  Copies the file src to 'to' under the new file name dest.
   300  
   301  If you want to copy the entire source directory to the destination
   302  one, you can do so like this:
   303  
   304  		var to Directory // the directory to copy to
   305  		for _, file := range dir.ListAll() {
   306  			dir.Copy(to, file, newFile, IO_CONTEXT_DEFAULT)
   307  			// newFile can be either file, or a new name
   308  		}
   309  
   310  NOTE: this method does not check whether dest exists and will
   311  overwrite it if it does.
   312  */
   313  func (d *DirectoryImpl) Copy(to Directory, src, dest string, ctx IOContext) (err error) {
   314  	var os IndexOutput
   315  	var is IndexInput
   316  	var success = false
   317  	defer func() {
   318  		if success {
   319  			err = util.Close(os, is)
   320  		} else {
   321  			util.CloseWhileSuppressingError(os, is)
   322  		}
   323  		defer func() {
   324  			recover() // ignore panic
   325  		}()
   326  		to.DeleteFile(dest) // ignore error
   327  	}()
   328  
   329  	os, err = to.CreateOutput(dest, ctx)
   330  	if err != nil {
   331  		return err
   332  	}
   333  	is, err = d.spi.OpenInput(src, ctx)
   334  	if err != nil {
   335  		return err
   336  	}
   337  	err = os.CopyBytes(is, is.Length())
   338  	if err != nil {
   339  		return err
   340  	}
   341  	success = true
   342  	return nil
   343  }
   344  
   345  // func (d *DirectoryImpl) CreateSlicer(name string, context IOContext) (is IndexInputSlicer, err error) {
   346  // 	d.EnsureOpen()
   347  // 	base, err := d.OpenInput(name, context)
   348  // 	if err != nil {
   349  // 		return nil, err
   350  // 	}
   351  // 	return simpleIndexInputSlicer{base}, nil
   352  // }
   353  
   354  // func (d *DirectoryImpl) EnsureOpen() {
   355  // 	if !d.IsOpen {
   356  // 		log.Print("This Directory is closed.")
   357  // 		panic("this Directory is closed")
   358  // 	}
   359  // }
   360  
   361  // type IndexInputSlicer interface {
   362  // 	io.Closer
   363  // 	OpenSlice(desc string, offset, length int64) IndexInput
   364  // 	OpenFullSlice() IndexInput
   365  // }
   366  
   367  // type simpleIndexInputSlicer struct {
   368  // 	base IndexInput
   369  // }
   370  
   371  // func (is simpleIndexInputSlicer) OpenSlice(desc string, offset, length int64) IndexInput {
   372  // 	return newSlicedIndexInput(fmt.Sprintf("SlicedIndexInput(%v in %v)", desc, is.base),
   373  // 		is.base, offset, length)
   374  // }
   375  
   376  // func (is simpleIndexInputSlicer) Close() error {
   377  // 	return is.base.Close()
   378  // }
   379  
   380  // func (is simpleIndexInputSlicer) OpenFullSlice() IndexInput {
   381  // 	return is.base
   382  // }
   383  
   384  // type SlicedIndexInput struct {
   385  // 	*BufferedIndexInput
   386  // 	base       IndexInput
   387  // 	fileOffset int64
   388  // 	length     int64
   389  // }
   390  
   391  // func newSlicedIndexInput(desc string, base IndexInput, fileOffset, length int64) *SlicedIndexInput {
   392  // 	return newSlicedIndexInputBySize(desc, base, fileOffset, length, BUFFER_SIZE)
   393  // }
   394  
   395  // func newSlicedIndexInputBySize(desc string, base IndexInput, fileOffset, length int64, bufferSize int) *SlicedIndexInput {
   396  // 	ans := &SlicedIndexInput{base: base, fileOffset: fileOffset, length: length}
   397  // 	ans.BufferedIndexInput = newBufferedIndexInputBySize(ans, fmt.Sprintf(
   398  // 		"SlicedIndexInput(%v in %v slice=%v:%v)",
   399  // 		desc, base, fileOffset, fileOffset+length), bufferSize)
   400  // 	return ans
   401  // }
   402  
   403  // func (in *SlicedIndexInput) readInternal(buf []byte) (err error) {
   404  // 	start := in.FilePointer()
   405  // 	if start+int64(len(buf)) > in.length {
   406  // 		return errors.New(fmt.Sprintf("read past EOF: %v", in))
   407  // 	}
   408  // 	in.base.Seek(in.fileOffset + start)
   409  // 	return in.base.ReadBytesBuffered(buf, false)
   410  // }
   411  
   412  // func (in *SlicedIndexInput) seekInternal(pos int64) error {
   413  // 	return nil // nothing
   414  // }
   415  
   416  // func (in *SlicedIndexInput) Close() error {
   417  // 	return in.base.Close()
   418  // }
   419  
   420  // func (in *SlicedIndexInput) Length() int64 {
   421  // 	return in.length
   422  // }
   423  
   424  // func (in *SlicedIndexInput) Clone() IndexInput {
   425  // 	return &SlicedIndexInput{
   426  // 		in.BufferedIndexInput.Clone(),
   427  // 		in.base.Clone(),
   428  // 		in.fileOffset,
   429  // 		in.length,
   430  // 	}
   431  // }
   432  
   433  // func (in *SlicedIndexInput) String() string {
   434  // 	return in.desc
   435  // }