github.com/balzaczyy/golucene@v0.0.0-20151210033525-d0be9ee89713/core/store/simplefs.go (about) 1 package store 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "path/filepath" 8 "sync" 9 ) 10 11 // store/SimpleFSLockFactory.java 12 13 type SimpleFSLock struct { 14 *LockImpl 15 file, dir string 16 } 17 18 func newSimpleFSLock(lockDir, lockFileName string) *SimpleFSLock { 19 ans := &SimpleFSLock{ 20 dir: lockDir, 21 file: filepath.Join(lockDir, lockFileName), 22 } 23 ans.LockImpl = NewLockImpl(ans) 24 return ans 25 } 26 27 func (lock *SimpleFSLock) Obtain() (ok bool, err error) { 28 // Ensure that lockDir exists and is a directory: 29 var fi os.FileInfo 30 fi, err = os.Stat(lock.dir) 31 if err == nil { // exists 32 if !fi.IsDir() { 33 err = errors.New(fmt.Sprintf("Found regular file where directory expected: %v", lock.dir)) 34 return 35 } 36 } else if os.IsNotExist(err) { 37 err = os.Mkdir(lock.dir, 0755) 38 if err != nil { // IO error 39 return 40 } 41 } else { // IO error 42 return 43 } 44 var f *os.File 45 if f, err = os.Create(lock.file); err == nil { 46 fmt.Printf("File '%v' is created.\n", f.Name()) 47 ok = true 48 defer f.Close() 49 } 50 return 51 52 } 53 54 func (lock *SimpleFSLock) Close() error { 55 return os.Remove(lock.file) 56 } 57 58 func (lock *SimpleFSLock) IsLocked() bool { 59 f, err := os.Open(lock.file) 60 if err == nil { 61 defer f.Close() 62 } 63 return err == nil || os.IsExist(err) 64 } 65 66 func (lock *SimpleFSLock) String() string { 67 return fmt.Sprintf("SimpleFSLock@%v", lock.file) 68 } 69 70 /* 71 Implements LockFactory using os.Create(). 72 73 NOTE: This API may has the same issue as the one in Lucene Java that 74 the write lock may not be released when Go program exists abnormally. 75 76 When this happens, an error is returned when trying to create a 77 writer, in which case you need to explicitly clear the lock file 78 first. You can either manually remove the file, or use 79 UnlockDirectory() API. But, first be certain that no writer is in 80 fact writing to the index otherwise you can easily corrupt your index. 81 82 If you suspect that this or any other LockFactory is not working 83 properly in your environment, you can easily test it by using 84 VerifyingLockFactory, LockVerifyServer and LockStressTest. 85 */ 86 type SimpleFSLockFactory struct { 87 *FSLockFactory 88 } 89 90 func NewSimpleFSLockFactory(path string) *SimpleFSLockFactory { 91 ans := &SimpleFSLockFactory{} 92 ans.FSLockFactory = newFSLockFactory() 93 ans.setLockDir(path) 94 return ans 95 } 96 97 func (f *SimpleFSLockFactory) Make(name string) Lock { 98 if f.lockPrefix != "" { 99 name = fmt.Sprintf("%v-%v", f.lockPrefix, name) 100 } 101 return newSimpleFSLock(f.lockDir, name) 102 } 103 104 func (f *SimpleFSLockFactory) Clear(name string) error { 105 if f.lockPrefix != "" { 106 name = fmt.Sprintf("%v-%v", f.lockPrefix, name) 107 } 108 return os.Remove(filepath.Join(f.lockDir, name)) 109 } 110 111 type SimpleFSDirectory struct { 112 *FSDirectory 113 } 114 115 func NewSimpleFSDirectory(path string) (d *SimpleFSDirectory, err error) { 116 d = &SimpleFSDirectory{} 117 d.FSDirectory, err = newFSDirectory(d, path) 118 if err != nil { 119 return nil, err 120 } 121 return 122 } 123 124 func (d *SimpleFSDirectory) OpenInput(name string, context IOContext) (IndexInput, error) { 125 d.EnsureOpen() 126 fpath := filepath.Join(d.path, name) 127 // fmt.Printf("Opening %v...\n", fpath) 128 return newSimpleFSIndexInput(fmt.Sprintf("SimpleFSIndexInput(path='%v')", fpath), fpath, context) 129 } 130 131 // func (d *SimpleFSDirectory) CreateSlicer(name string, ctx IOContext) (slicer IndexInputSlicer, err error) { 132 // d.EnsureOpen() 133 // f, err := os.Open(filepath.Join(d.path, name)) 134 // if err != nil { 135 // return nil, err 136 // } 137 // return &fileIndexInputSlicer{f, ctx, d.chunkSize}, nil 138 // } 139 140 // type fileIndexInputSlicer struct { 141 // file *os.File 142 // ctx IOContext 143 // chunkSize int 144 // } 145 146 // func (s *fileIndexInputSlicer) Close() error { 147 // err := s.file.Close() 148 // if err != nil { 149 // fmt.Printf("Closing %v failed: %v\n", s.file.Name(), err) 150 // } 151 // return err 152 // } 153 154 // func (s *fileIndexInputSlicer) OpenSlice(desc string, offset, length int64) IndexInput { 155 // return newSimpleFSIndexInputFromFileSlice(fmt.Sprintf("SimpleFSIndexInput(%v in path='%v' slice=%v:%v)", 156 // desc, s.file.Name(), offset, offset+length), 157 // s.file, offset, length, bufferSize(s.ctx), s.chunkSize) 158 // } 159 160 // func (s *fileIndexInputSlicer) OpenFullSlice() IndexInput { 161 // fi, err := s.file.Stat() 162 // if err != nil { 163 // panic(err) 164 // } 165 // return s.OpenSlice("full-slice", 0, fi.Size()) 166 // } 167 168 /* 169 The maximum chunk size is 8192 bytes, becaues Java RamdomAccessFile 170 mallocs a native buffer outside of stack if the read buffer size is 171 larger. GoLucene takes the same default value. 172 TODO: test larger value here 173 */ 174 const CHUNK_SIZE = 8192 175 176 type SimpleFSIndexInput struct { 177 *BufferedIndexInput 178 fileLock sync.Locker 179 // the file channel we will read from 180 file *os.File 181 // is this instance a clone and hence does not own the file to close it 182 isClone bool 183 // start offset: non-zero in the slice case 184 off int64 185 // end offset (start+length) 186 end int64 187 } 188 189 func newSimpleFSIndexInput(desc, path string, ctx IOContext) (*SimpleFSIndexInput, error) { 190 f, err := os.Open(path) 191 if err != nil { 192 return nil, err 193 } 194 fstat, err := f.Stat() 195 if err != nil { 196 return nil, err 197 } 198 ans := new(SimpleFSIndexInput) 199 ans.BufferedIndexInput = newBufferedIndexInput(ans, desc, ctx) 200 ans.file = f 201 ans.off = 0 202 ans.end = fstat.Size() 203 ans.fileLock = &sync.Mutex{} 204 return ans, nil 205 } 206 207 func newSimpleFSIndexInputFromFileSlice(desc string, file *os.File, off, length int64, bufferSize int) *SimpleFSIndexInput { 208 ans := new(SimpleFSIndexInput) 209 ans.BufferedIndexInput = newBufferedIndexInputBySize(ans, desc, bufferSize) 210 ans.file = file 211 ans.off = off 212 ans.end = off + length 213 ans.isClone = true 214 return ans 215 } 216 217 func (in *SimpleFSIndexInput) Close() error { 218 if !in.isClone { 219 return in.file.Close() 220 } 221 return nil 222 } 223 224 func (in *SimpleFSIndexInput) Clone() IndexInput { 225 ans := &SimpleFSIndexInput{ 226 in.BufferedIndexInput.Clone(), 227 in.fileLock, 228 in.file, 229 true, 230 in.off, 231 in.end, 232 } 233 ans.spi = ans 234 return ans 235 } 236 237 func (in *SimpleFSIndexInput) Slice(desc string, offset, length int64) (IndexInput, error) { 238 assert2(offset >= 0 && length >= 0 && offset+length <= in.Length(), 239 "slice() %v out of bounds: %v", desc, in) 240 ans := newSimpleFSIndexInputFromFileSlice(desc, in.file, in.off+offset, length, in.bufferSize) 241 ans.fileLock = in.fileLock // share same file lock 242 return ans, nil 243 } 244 245 func (in *SimpleFSIndexInput) Length() int64 { 246 return in.end - in.off 247 } 248 249 func (in *SimpleFSIndexInput) readInternal(buf []byte) error { 250 length := len(buf) 251 in.fileLock.Lock() 252 defer in.fileLock.Unlock() 253 254 // TODO make use of Go's relative Seek or ReadAt function 255 position := in.off + in.FilePointer() 256 _, err := in.file.Seek(position, 0) 257 if err != nil { 258 return err 259 } 260 261 if position+int64(length) > in.end { 262 return errors.New(fmt.Sprintf("read past EOF: %v", in)) 263 } 264 265 total := 0 266 for { 267 readLength := length - total 268 if CHUNK_SIZE < readLength { 269 readLength = CHUNK_SIZE 270 } 271 // FIXME verify slice is working 272 i, err := in.file.Read(buf[total : total+readLength]) 273 if err != nil { 274 return errors.New(fmt.Sprintf("%v: %v", err, in)) 275 } 276 total += i 277 if total >= length { 278 break 279 } 280 } 281 return nil 282 } 283 284 func (in *SimpleFSIndexInput) seekInternal(pos int64) error { return nil }