github.com/TeaOSLab/EdgeNode@v1.3.8/internal/utils/bfs/meta_file.go (about) 1 // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn . 2 3 package bfs 4 5 import ( 6 "bytes" 7 "encoding/binary" 8 "github.com/TeaOSLab/EdgeNode/internal/utils/fasttime" 9 "github.com/TeaOSLab/EdgeNode/internal/zero" 10 "io" 11 "os" 12 "sync" 13 ) 14 15 const MFileExt = ".m" 16 const Version1 = 1 17 18 type MetaFile struct { 19 fp *os.File 20 filename string 21 headerMap map[string]*LazyFileHeader // hash => *LazyFileHeader 22 mu *sync.RWMutex // TODO 考虑单独一个,不要和bFile共享? 23 24 isModified bool 25 modifiedHashMap map[string]zero.Zero // hash => Zero 26 } 27 28 func OpenMetaFile(filename string, mu *sync.RWMutex) (*MetaFile, error) { 29 fp, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, 0666) 30 if err != nil { 31 return nil, err 32 } 33 34 var mFile = &MetaFile{ 35 filename: filename, 36 fp: fp, 37 headerMap: map[string]*LazyFileHeader{}, 38 mu: mu, 39 modifiedHashMap: map[string]zero.Zero{}, 40 } 41 42 // 从文件中加载已有的文件头信息 43 err = mFile.load() 44 if err != nil { 45 return nil, err 46 } 47 48 return mFile, nil 49 } 50 51 func (this *MetaFile) load() error { 52 AckReadThread() 53 _, err := this.fp.Seek(0, io.SeekStart) 54 ReleaseReadThread() 55 if err != nil { 56 return err 57 } 58 59 // TODO 检查文件是否完整 60 61 var buf = make([]byte, 4<<10) 62 var blockBytes []byte 63 for { 64 AckReadThread() 65 n, readErr := this.fp.Read(buf) 66 ReleaseReadThread() 67 if n > 0 { 68 blockBytes = append(blockBytes, buf[:n]...) 69 for len(blockBytes) > 4 { 70 var l = int(binary.BigEndian.Uint32(blockBytes[:4])) + 4 /* Len **/ 71 if len(blockBytes) < l { 72 break 73 } 74 75 action, hash, data, decodeErr := DecodeMetaBlock(blockBytes[:l]) 76 if decodeErr != nil { 77 return decodeErr 78 } 79 80 switch action { 81 case MetaActionNew: 82 this.headerMap[hash] = NewLazyFileHeaderFromData(data) 83 case MetaActionRemove: 84 delete(this.headerMap, hash) 85 } 86 87 blockBytes = blockBytes[l:] 88 } 89 } 90 if readErr != nil { 91 if readErr == io.EOF { 92 break 93 } 94 return readErr 95 } 96 } 97 98 return nil 99 } 100 101 func (this *MetaFile) WriteMeta(hash string, status int, expiresAt int64, expectedFileSize int64) error { 102 103 this.mu.Lock() 104 defer this.mu.Unlock() 105 106 this.headerMap[hash] = NewLazyFileHeader(&FileHeader{ 107 Version: Version1, 108 ExpiresAt: expiresAt, 109 Status: status, 110 ExpiredBodySize: expectedFileSize, 111 IsWriting: true, 112 }) 113 114 this.modifiedHashMap[hash] = zero.Zero{} 115 116 return nil 117 } 118 119 func (this *MetaFile) WriteHeaderBlockUnsafe(hash string, bOffsetFrom int64, bOffsetTo int64) error { 120 lazyHeader, ok := this.headerMap[hash] 121 if !ok { 122 return nil 123 } 124 125 header, err := lazyHeader.FileHeaderUnsafe() 126 if err != nil { 127 return err 128 } 129 130 // TODO 合并相邻block 131 header.HeaderBlocks = append(header.HeaderBlocks, BlockInfo{ 132 BFileOffsetFrom: bOffsetFrom, 133 BFileOffsetTo: bOffsetTo, 134 }) 135 136 this.modifiedHashMap[hash] = zero.Zero{} 137 138 return nil 139 } 140 141 func (this *MetaFile) WriteBodyBlockUnsafe(hash string, bOffsetFrom int64, bOffsetTo int64, originOffsetFrom int64, originOffsetTo int64) error { 142 lazyHeader, ok := this.headerMap[hash] 143 if !ok { 144 return nil 145 } 146 147 header, err := lazyHeader.FileHeaderUnsafe() 148 if err != nil { 149 return err 150 } 151 152 // TODO 合并相邻block 153 header.BodyBlocks = append(header.BodyBlocks, BlockInfo{ 154 OriginOffsetFrom: originOffsetFrom, 155 OriginOffsetTo: originOffsetTo, 156 BFileOffsetFrom: bOffsetFrom, 157 BFileOffsetTo: bOffsetTo, 158 }) 159 160 this.modifiedHashMap[hash] = zero.Zero{} 161 162 return nil 163 } 164 165 func (this *MetaFile) WriteClose(hash string, headerSize int64, bodySize int64) error { 166 // TODO 考虑单个hash多次重复调用的情况 167 168 this.mu.Lock() 169 lazyHeader, ok := this.headerMap[hash] 170 if !ok { 171 this.mu.Unlock() 172 return nil 173 } 174 175 header, err := lazyHeader.FileHeaderUnsafe() 176 if err != nil { 177 return err 178 } 179 180 this.mu.Unlock() 181 182 // TODO 检查bodySize和expectedBodySize是否一致,如果不一致则从headerMap中删除 183 184 header.ModifiedAt = fasttime.Now().Unix() 185 header.HeaderSize = headerSize 186 header.BodySize = bodySize 187 header.Compact() 188 189 blockBytes, err := header.Encode(hash) 190 if err != nil { 191 return err 192 } 193 194 this.mu.Lock() 195 defer this.mu.Unlock() 196 197 AckReadThread() 198 _, err = this.fp.Seek(0, io.SeekEnd) 199 ReleaseReadThread() 200 if err != nil { 201 return err 202 } 203 204 AckWriteThread() 205 _, err = this.fp.Write(blockBytes) 206 ReleaseWriteThread() 207 208 this.isModified = true 209 return err 210 } 211 212 func (this *MetaFile) RemoveFile(hash string) error { 213 this.mu.Lock() 214 defer this.mu.Unlock() 215 216 _, ok := this.headerMap[hash] 217 if ok { 218 delete(this.headerMap, hash) 219 } 220 221 if ok { 222 blockBytes, err := EncodeMetaBlock(MetaActionRemove, hash, nil) 223 if err != nil { 224 return err 225 } 226 227 AckWriteThread() 228 _, err = this.fp.Write(blockBytes) 229 ReleaseWriteThread() 230 if err != nil { 231 return err 232 } 233 this.isModified = true 234 } 235 236 return nil 237 } 238 239 func (this *MetaFile) FileHeader(hash string) (header *FileHeader, ok bool) { 240 this.mu.RLock() 241 defer this.mu.RUnlock() 242 243 lazyHeader, ok := this.headerMap[hash] 244 245 if ok { 246 var err error 247 header, err = lazyHeader.FileHeaderUnsafe() 248 if err != nil { 249 ok = false 250 } 251 } 252 return 253 } 254 255 func (this *MetaFile) FileHeaderUnsafe(hash string) (header *FileHeader, ok bool) { 256 lazyHeader, ok := this.headerMap[hash] 257 258 if ok { 259 var err error 260 header, err = lazyHeader.FileHeaderUnsafe() 261 if err != nil { 262 ok = false 263 } 264 } 265 266 return 267 } 268 269 func (this *MetaFile) CloneFileHeader(hash string) (header *FileHeader, ok bool) { 270 this.mu.RLock() 271 defer this.mu.RUnlock() 272 lazyHeader, ok := this.headerMap[hash] 273 if !ok { 274 return 275 } 276 277 var err error 278 header, err = lazyHeader.FileHeaderUnsafe() 279 if err != nil { 280 ok = false 281 return 282 } 283 284 header = header.Clone() 285 return 286 } 287 288 func (this *MetaFile) FileHeaders() map[string]*LazyFileHeader { 289 this.mu.RLock() 290 defer this.mu.RUnlock() 291 return this.headerMap 292 } 293 294 func (this *MetaFile) ExistFile(hash string) bool { 295 this.mu.RLock() 296 defer this.mu.RUnlock() 297 298 _, ok := this.headerMap[hash] 299 return ok 300 } 301 302 // Compact the meta file 303 // TODO 考虑自动Compact的时机(脏数据比例?) 304 func (this *MetaFile) Compact() error { 305 this.mu.Lock() 306 defer this.mu.Unlock() 307 308 var buf = bytes.NewBuffer(nil) 309 for hash, lazyHeader := range this.headerMap { 310 header, err := lazyHeader.FileHeaderUnsafe() 311 if err != nil { 312 return err 313 } 314 315 blockBytes, err := header.Encode(hash) 316 if err != nil { 317 return err 318 } 319 buf.Write(blockBytes) 320 } 321 322 AckWriteThread() 323 err := this.fp.Truncate(int64(buf.Len())) 324 ReleaseWriteThread() 325 if err != nil { 326 return err 327 } 328 329 AckReadThread() 330 _, err = this.fp.Seek(0, io.SeekStart) 331 ReleaseReadThread() 332 if err != nil { 333 return err 334 } 335 336 AckWriteThread() 337 _, err = this.fp.Write(buf.Bytes()) 338 ReleaseWriteThread() 339 this.isModified = true 340 return err 341 } 342 343 func (this *MetaFile) SyncUnsafe() error { 344 if !this.isModified { 345 return nil 346 } 347 348 AckWriteThread() 349 err := this.fp.Sync() 350 ReleaseWriteThread() 351 if err != nil { 352 return err 353 } 354 355 for hash := range this.modifiedHashMap { 356 lazyHeader, ok := this.headerMap[hash] 357 if ok { 358 header, decodeErr := lazyHeader.FileHeaderUnsafe() 359 if decodeErr != nil { 360 return decodeErr 361 } 362 header.IsWriting = false 363 } 364 } 365 366 this.isModified = false 367 this.modifiedHashMap = map[string]zero.Zero{} 368 return nil 369 } 370 371 // Close 关闭当前文件 372 func (this *MetaFile) Close() error { 373 return this.fp.Close() 374 } 375 376 // RemoveAll 删除所有数据 377 func (this *MetaFile) RemoveAll() error { 378 _ = this.fp.Close() 379 return os.Remove(this.fp.Name()) 380 }