2020-03-30 12:07:55 +08:00
package filer2
2020-03-27 19:50:51 +08:00
import (
"context"
"fmt"
"io"
"sync"
"github.com/chrislusf/seaweedfs/weed/glog"
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
2020-04-12 03:45:24 +08:00
"github.com/chrislusf/seaweedfs/weed/util/chunk_cache"
2020-03-27 19:50:51 +08:00
"github.com/chrislusf/seaweedfs/weed/wdclient"
)
type ChunkReadAt struct {
masterClient * wdclient . MasterClient
2020-03-30 12:07:55 +08:00
chunkViews [ ] * ChunkView
2020-03-27 19:50:51 +08:00
lookupFileId func ( fileId string ) ( targetUrl string , err error )
readerLock sync . Mutex
2020-08-16 15:49:08 +08:00
fileSize int64
2020-03-29 04:43:31 +08:00
2020-08-18 11:20:08 +08:00
chunkCache chunk_cache . ChunkCache
2020-03-27 19:50:51 +08:00
}
// var _ = io.ReaderAt(&ChunkReadAt{})
2020-04-30 08:40:08 +08:00
type LookupFileIdFunctionType func ( fileId string ) ( targetUrl string , err error )
func LookupFn ( filerClient filer_pb . FilerClient ) LookupFileIdFunctionType {
return func ( fileId string ) ( targetUrl string , err error ) {
err = filerClient . WithFilerClient ( func ( client filer_pb . SeaweedFilerClient ) error {
vid := VolumeId ( fileId )
resp , err := client . LookupVolume ( context . Background ( ) , & filer_pb . LookupVolumeRequest {
VolumeIds : [ ] string { vid } ,
} )
if err != nil {
return err
}
locations := resp . LocationsMap [ vid ]
if locations == nil || len ( locations . Locations ) == 0 {
glog . V ( 0 ) . Infof ( "failed to locate %s" , fileId )
return fmt . Errorf ( "failed to locate %s" , fileId )
}
volumeServerAddress := filerClient . AdjustedUrl ( locations . Locations [ 0 ] . Url )
targetUrl = fmt . Sprintf ( "http://%s/%s" , volumeServerAddress , fileId )
return nil
} )
return
}
}
2020-08-18 11:20:08 +08:00
func NewChunkReaderAtFromClient ( filerClient filer_pb . FilerClient , chunkViews [ ] * ChunkView , chunkCache chunk_cache . ChunkCache , fileSize int64 ) * ChunkReadAt {
2020-03-27 19:50:51 +08:00
return & ChunkReadAt {
2020-05-10 18:50:30 +08:00
chunkViews : chunkViews ,
2020-04-30 08:40:08 +08:00
lookupFileId : LookupFn ( filerClient ) ,
2020-03-29 05:07:16 +08:00
chunkCache : chunkCache ,
2020-08-16 15:49:08 +08:00
fileSize : fileSize ,
2020-03-27 19:50:51 +08:00
}
}
func ( c * ChunkReadAt ) ReadAt ( p [ ] byte , offset int64 ) ( n int , err error ) {
c . readerLock . Lock ( )
defer c . readerLock . Unlock ( )
2020-08-18 07:05:40 +08:00
glog . V ( 4 ) . Infof ( "ReadAt [%d,%d) of total file size %d bytes %d chunk views" , offset , offset + int64 ( len ( p ) ) , c . fileSize , len ( c . chunkViews ) )
2020-08-18 11:14:40 +08:00
return c . doReadAt ( p [ n : ] , offset + int64 ( n ) )
2020-03-27 19:50:51 +08:00
}
func ( c * ChunkReadAt ) doReadAt ( p [ ] byte , offset int64 ) ( n int , err error ) {
2020-08-18 11:14:40 +08:00
var buffer [ ] byte
startOffset , remaining := offset , int64 ( len ( p ) )
2020-08-18 07:05:40 +08:00
for i , chunk := range c . chunkViews {
2020-08-18 11:14:40 +08:00
if remaining <= 0 {
break
}
if startOffset < chunk . LogicOffset {
gap := int ( chunk . LogicOffset - startOffset )
2020-08-18 15:32:01 +08:00
glog . V ( 4 ) . Infof ( "zero [%d,%d)" , startOffset , startOffset + int64 ( gap ) )
n += int ( min ( int64 ( gap ) , remaining ) )
2020-08-18 11:14:40 +08:00
startOffset , remaining = chunk . LogicOffset , remaining - int64 ( gap )
if remaining <= 0 {
break
}
2020-08-17 12:07:46 +08:00
}
2020-08-17 06:16:46 +08:00
// fmt.Printf(">>> doReadAt [%d,%d), chunk[%d,%d)\n", offset, offset+int64(len(p)), chunk.LogicOffset, chunk.LogicOffset+int64(chunk.Size))
2020-08-18 11:14:40 +08:00
chunkStart , chunkStop := max ( chunk . LogicOffset , startOffset ) , min ( chunk . LogicOffset + int64 ( chunk . Size ) , startOffset + remaining )
2020-08-17 12:07:46 +08:00
if chunkStart >= chunkStop {
continue
}
2020-08-18 11:14:40 +08:00
glog . V ( 4 ) . Infof ( "read [%d,%d), %d/%d chunk %s [%d,%d)" , chunkStart , chunkStop , i , len ( c . chunkViews ) , chunk . FileId , chunk . LogicOffset - chunk . Offset , chunk . LogicOffset - chunk . Offset + int64 ( chunk . Size ) )
buffer , err = c . readFromWholeChunkData ( chunk )
2020-08-17 12:07:46 +08:00
if err != nil {
glog . Errorf ( "fetching chunk %+v: %v\n" , chunk , err )
return
2020-03-27 19:50:51 +08:00
}
2020-08-17 12:07:46 +08:00
bufferOffset := chunkStart - chunk . LogicOffset + chunk . Offset
2020-08-18 12:17:32 +08:00
copied := copy ( p [ startOffset - offset : chunkStop - chunkStart + startOffset - offset ] , buffer [ bufferOffset : bufferOffset + chunkStop - chunkStart ] )
2020-08-17 12:07:46 +08:00
n += copied
2020-08-18 11:20:08 +08:00
startOffset , remaining = startOffset + int64 ( copied ) , remaining - int64 ( copied )
2020-03-27 19:50:51 +08:00
}
2020-08-16 15:49:08 +08:00
2020-08-18 11:14:40 +08:00
glog . V ( 4 ) . Infof ( "doReadAt [%d,%d), n:%v, err:%v" , offset , offset + int64 ( len ( p ) ) , n , err )
2020-08-16 15:49:08 +08:00
2020-08-18 13:46:32 +08:00
if remaining > 0 && c . fileSize > startOffset {
delta := int ( min ( remaining , c . fileSize - startOffset ) )
2020-08-18 15:34:15 +08:00
glog . V ( 4 ) . Infof ( "zero2 [%d,%d) of file size %d bytes" , startOffset , startOffset + int64 ( delta ) , c . fileSize )
2020-08-18 13:46:32 +08:00
n += delta
2020-03-27 19:50:51 +08:00
}
2020-08-18 11:14:40 +08:00
2020-08-18 23:18:54 +08:00
if err == nil && offset + int64 ( len ( p ) ) > c . fileSize {
2020-08-16 15:49:08 +08:00
err = io . EOF
}
2020-08-17 06:16:46 +08:00
// fmt.Printf("~~~ filled %d, err: %v\n\n", n, err)
2020-03-27 19:50:51 +08:00
return
}
2020-08-18 11:14:40 +08:00
func ( c * ChunkReadAt ) readFromWholeChunkData ( chunkView * ChunkView ) ( chunkData [ ] byte , err error ) {
2020-03-27 19:50:51 +08:00
2020-08-18 11:14:40 +08:00
glog . V ( 4 ) . Infof ( "readFromWholeChunkData %s offset %d [%d,%d) size at least %d" , chunkView . FileId , chunkView . Offset , chunkView . LogicOffset , chunkView . LogicOffset + int64 ( chunkView . Size ) , chunkView . ChunkSize )
2020-03-29 04:43:31 +08:00
2020-08-17 06:16:46 +08:00
chunkData = c . chunkCache . GetChunk ( chunkView . FileId , chunkView . ChunkSize )
2020-03-29 04:43:31 +08:00
if chunkData != nil {
2020-08-17 12:07:46 +08:00
glog . V ( 5 ) . Infof ( "cache hit %s [%d,%d)" , chunkView . FileId , chunkView . LogicOffset - chunkView . Offset , chunkView . LogicOffset - chunkView . Offset + int64 ( len ( chunkData ) ) )
2020-03-29 15:54:39 +08:00
} else {
2020-08-18 07:05:40 +08:00
glog . V ( 4 ) . Infof ( "doFetchFullChunkData %s" , chunkView . FileId )
2020-03-29 15:54:39 +08:00
chunkData , err = c . doFetchFullChunkData ( chunkView . FileId , chunkView . CipherKey , chunkView . IsGzipped )
if err != nil {
2020-08-17 06:16:46 +08:00
return
2020-03-29 15:54:39 +08:00
}
2020-04-12 16:13:57 +08:00
c . chunkCache . SetChunk ( chunkView . FileId , chunkData )
}
2020-03-29 15:54:39 +08:00
2020-08-17 06:16:46 +08:00
return
2020-03-29 15:54:39 +08:00
}
func ( c * ChunkReadAt ) doFetchFullChunkData ( fileId string , cipherKey [ ] byte , isGzipped bool ) ( [ ] byte , error ) {
2020-07-20 08:59:43 +08:00
return fetchChunk ( c . lookupFileId , fileId , cipherKey , isGzipped )
2020-03-27 19:50:51 +08:00
}