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 // }