chrislu
2 years ago
8 changed files with 321 additions and 83 deletions
-
152weed/filer/filechunk_group.go
-
36weed/filer/filechunk_group_test.go
-
94weed/filer/filechunk_section.go
-
1weed/mount/dirty_pages_chunked.go
-
59weed/mount/filehandle.go
-
22weed/mount/filehandle_read.go
-
2weed/mount/weedfs_attr.go
-
38weed/mount/weedfs_file_lseek.go
@ -0,0 +1,152 @@ |
|||
package filer |
|||
|
|||
import ( |
|||
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" |
|||
"github.com/seaweedfs/seaweedfs/weed/util/chunk_cache" |
|||
"github.com/seaweedfs/seaweedfs/weed/wdclient" |
|||
"sync" |
|||
) |
|||
|
|||
type ChunkGroup struct { |
|||
lookupFn wdclient.LookupFileIdFunctionType |
|||
chunkCache chunk_cache.ChunkCache |
|||
manifestChunks []*filer_pb.FileChunk |
|||
sections map[SectionIndex]*FileChunkSection |
|||
sectionsLock sync.RWMutex |
|||
} |
|||
|
|||
func NewChunkGroup(lookupFn wdclient.LookupFileIdFunctionType, chunkCache chunk_cache.ChunkCache, chunks []*filer_pb.FileChunk) (*ChunkGroup, error) { |
|||
group := &ChunkGroup{ |
|||
lookupFn: lookupFn, |
|||
chunkCache: chunkCache, |
|||
sections: make(map[SectionIndex]*FileChunkSection), |
|||
} |
|||
|
|||
err := group.SetChunks(chunks) |
|||
return group, err |
|||
} |
|||
|
|||
func (group *ChunkGroup) AddChunk(chunk *filer_pb.FileChunk) error { |
|||
|
|||
group.sectionsLock.Lock() |
|||
defer group.sectionsLock.Unlock() |
|||
|
|||
sectionIndexStart, sectionIndexStop := SectionIndex(chunk.Offset/SectionSize), SectionIndex((chunk.Offset+int64(chunk.Size))/SectionSize) |
|||
for si := sectionIndexStart; si < sectionIndexStop+1; si++ { |
|||
section, found := group.sections[si] |
|||
if !found { |
|||
section = &FileChunkSection{ |
|||
sectionIndex: si, |
|||
} |
|||
group.sections[si] = section |
|||
} |
|||
section.addChunk(chunk) |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func (group *ChunkGroup) ReadDataAt(fileSize int64, buff []byte, offset int64) (n int, tsNs int64, err error) { |
|||
|
|||
group.sectionsLock.RLock() |
|||
defer group.sectionsLock.RUnlock() |
|||
|
|||
sectionIndexStart, sectionIndexStop := SectionIndex(offset/SectionSize), SectionIndex((offset+int64(len(buff)))/SectionSize) |
|||
for si := sectionIndexStart; si < sectionIndexStop+1; si++ { |
|||
section, found := group.sections[si] |
|||
rangeStart, rangeStop := max(offset, int64(si*SectionSize)), min(offset+int64(len(buff)), int64((si+1)*SectionSize)) |
|||
if !found { |
|||
for i := rangeStart; i < rangeStop; i++ { |
|||
buff[i-offset] = 0 |
|||
} |
|||
continue |
|||
} |
|||
xn, xTsNs, xErr := section.readDataAt(group, fileSize, buff[rangeStart-offset:rangeStop-offset], rangeStart) |
|||
if xErr != nil { |
|||
err = xErr |
|||
} |
|||
n += xn |
|||
tsNs = max(tsNs, xTsNs) |
|||
} |
|||
return |
|||
} |
|||
|
|||
func (group *ChunkGroup) SetChunks(chunks []*filer_pb.FileChunk) error { |
|||
var dataChunks []*filer_pb.FileChunk |
|||
for _, chunk := range chunks { |
|||
|
|||
if !chunk.IsChunkManifest { |
|||
dataChunks = append(dataChunks, chunk) |
|||
continue |
|||
} |
|||
|
|||
resolvedChunks, err := ResolveOneChunkManifest(group.lookupFn, chunk) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
group.manifestChunks = append(group.manifestChunks, chunk) |
|||
dataChunks = append(dataChunks, resolvedChunks...) |
|||
} |
|||
|
|||
for _, chunk := range dataChunks { |
|||
sectionIndexStart, sectionIndexStop := SectionIndex(chunk.Offset/SectionSize), SectionIndex((chunk.Offset+int64(chunk.Size))/SectionSize) |
|||
for si := sectionIndexStart; si < sectionIndexStop+1; si++ { |
|||
section, found := group.sections[si] |
|||
if !found { |
|||
section = &FileChunkSection{ |
|||
sectionIndex: si, |
|||
} |
|||
group.sections[si] = section |
|||
} |
|||
section.chunks = append(section.chunks, chunk) |
|||
} |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
const ( |
|||
// see weedfs_file_lseek.go
|
|||
SEEK_DATA uint32 = 3 // seek to next data after the offset
|
|||
// SEEK_HOLE uint32 = 4 // seek to next hole after the offset
|
|||
) |
|||
|
|||
// FIXME: needa tests
|
|||
func (group *ChunkGroup) SearchChunks(offset, fileSize int64, whence uint32) (found bool, out int64) { |
|||
group.sectionsLock.RLock() |
|||
defer group.sectionsLock.RUnlock() |
|||
|
|||
return group.doSearchChunks(offset, fileSize, whence) |
|||
} |
|||
|
|||
func (group *ChunkGroup) doSearchChunks(offset, fileSize int64, whence uint32) (found bool, out int64) { |
|||
|
|||
sectionIndex, maxSectionIndex := SectionIndex(offset/SectionSize), SectionIndex(fileSize/SectionSize) |
|||
if whence == SEEK_DATA { |
|||
for si := sectionIndex; si < maxSectionIndex+1; si++ { |
|||
section, foundSection := group.sections[si] |
|||
if !foundSection { |
|||
continue |
|||
} |
|||
sectionStart := section.DataStartOffset(group, offset, fileSize) |
|||
if sectionStart == -1 { |
|||
continue |
|||
} |
|||
return true, sectionStart |
|||
} |
|||
return false, 0 |
|||
} else { |
|||
// whence == SEEK_HOLE
|
|||
for si := sectionIndex; si < maxSectionIndex; si++ { |
|||
section, foundSection := group.sections[si] |
|||
if !foundSection { |
|||
return true, offset |
|||
} |
|||
holeStart := section.NextStopOffset(group, offset, fileSize) |
|||
if holeStart%SectionSize == 0 { |
|||
continue |
|||
} |
|||
return true, holeStart |
|||
} |
|||
return true, fileSize |
|||
} |
|||
} |
@ -0,0 +1,36 @@ |
|||
package filer |
|||
|
|||
import ( |
|||
"github.com/stretchr/testify/assert" |
|||
"testing" |
|||
) |
|||
|
|||
func TestChunkGroup_doSearchChunks(t *testing.T) { |
|||
type fields struct { |
|||
sections map[SectionIndex]*FileChunkSection |
|||
} |
|||
type args struct { |
|||
offset int64 |
|||
fileSize int64 |
|||
whence uint32 |
|||
} |
|||
tests := []struct { |
|||
name string |
|||
fields fields |
|||
args args |
|||
wantFound bool |
|||
wantOut int64 |
|||
}{ |
|||
// TODO: Add test cases.
|
|||
} |
|||
for _, tt := range tests { |
|||
t.Run(tt.name, func(t *testing.T) { |
|||
group := &ChunkGroup{ |
|||
sections: tt.fields.sections, |
|||
} |
|||
gotFound, gotOut := group.doSearchChunks(tt.args.offset, tt.args.fileSize, tt.args.whence) |
|||
assert.Equalf(t, tt.wantFound, gotFound, "doSearchChunks(%v, %v, %v)", tt.args.offset, tt.args.fileSize, tt.args.whence) |
|||
assert.Equalf(t, tt.wantOut, gotOut, "doSearchChunks(%v, %v, %v)", tt.args.offset, tt.args.fileSize, tt.args.whence) |
|||
}) |
|||
} |
|||
} |
@ -0,0 +1,94 @@ |
|||
package filer |
|||
|
|||
import ( |
|||
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" |
|||
"sync" |
|||
) |
|||
|
|||
const SectionSize = 2 * 1024 * 1024 * 128 // 256MiB
|
|||
type SectionIndex int64 |
|||
type FileChunkSection struct { |
|||
sectionIndex SectionIndex |
|||
chunks []*filer_pb.FileChunk |
|||
entryViewCache []VisibleInterval |
|||
chunkViews []*ChunkView |
|||
reader *ChunkReadAt |
|||
lock sync.Mutex |
|||
} |
|||
|
|||
func (section *FileChunkSection) addChunk(chunk *filer_pb.FileChunk) error { |
|||
section.lock.Lock() |
|||
defer section.lock.Unlock() |
|||
section.chunks = append(section.chunks, chunk) |
|||
// FIXME: this can be improved to an incremental change
|
|||
section.entryViewCache = nil |
|||
return nil |
|||
} |
|||
|
|||
func (section *FileChunkSection) readDataAt(group *ChunkGroup, fileSize int64, buff []byte, offset int64) (n int, tsNs int64, err error) { |
|||
section.lock.Lock() |
|||
defer section.lock.Unlock() |
|||
|
|||
section.setupForRead(group, fileSize) |
|||
|
|||
return section.reader.ReadAtWithTime(buff, offset) |
|||
} |
|||
|
|||
func (section *FileChunkSection) setupForRead(group *ChunkGroup, fileSize int64) { |
|||
if section.entryViewCache == nil { |
|||
section.entryViewCache = readResolvedChunks(section.chunks) |
|||
section.chunks, _ = SeparateGarbageChunks(section.entryViewCache, section.chunks) |
|||
if section.reader != nil { |
|||
_ = section.reader.Close() |
|||
section.reader = nil |
|||
} |
|||
} |
|||
|
|||
if section.reader == nil { |
|||
chunkViews := ViewFromVisibleIntervals(section.entryViewCache, int64(section.sectionIndex)*SectionSize, (int64(section.sectionIndex)+1)*SectionSize) |
|||
section.reader = NewChunkReaderAtFromClient(group.lookupFn, chunkViews, group.chunkCache, min(int64(section.sectionIndex+1)*SectionSize, fileSize)) |
|||
} |
|||
} |
|||
|
|||
func (section *FileChunkSection) DataStartOffset(group *ChunkGroup, offset int64, fileSize int64) int64 { |
|||
section.lock.Lock() |
|||
defer section.lock.Unlock() |
|||
|
|||
section.setupForRead(group, fileSize) |
|||
|
|||
for _, visible := range section.entryViewCache { |
|||
if visible.stop <= offset { |
|||
continue |
|||
} |
|||
if offset < visible.start { |
|||
return offset |
|||
} |
|||
return offset |
|||
} |
|||
return -1 |
|||
} |
|||
|
|||
func (section *FileChunkSection) NextStopOffset(group *ChunkGroup, offset int64, fileSize int64) int64 { |
|||
section.lock.Lock() |
|||
defer section.lock.Unlock() |
|||
|
|||
section.setupForRead(group, fileSize) |
|||
|
|||
isAfterOffset := false |
|||
for _, visible := range section.entryViewCache { |
|||
if !isAfterOffset { |
|||
if visible.stop <= offset { |
|||
continue |
|||
} |
|||
isAfterOffset = true |
|||
} |
|||
if offset < visible.start { |
|||
return offset |
|||
} |
|||
// now visible.start <= offset
|
|||
if offset < visible.stop { |
|||
offset = visible.stop |
|||
} |
|||
} |
|||
return offset |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue