2021-03-22 04:05:53 +08:00
package storage
import (
"fmt"
2022-06-06 02:54:04 +08:00
"github.com/chrislusf/seaweedfs/weed/util/mem"
2021-03-22 04:05:53 +08:00
"io"
"time"
"github.com/chrislusf/seaweedfs/weed/glog"
"github.com/chrislusf/seaweedfs/weed/storage/backend"
"github.com/chrislusf/seaweedfs/weed/storage/needle"
"github.com/chrislusf/seaweedfs/weed/storage/super_block"
. "github.com/chrislusf/seaweedfs/weed/storage/types"
)
2022-06-06 02:54:04 +08:00
const PagedReadLimit = 1024 * 1024
2021-03-22 04:05:53 +08:00
// read fills in Needle content by looking up n.Id from NeedleMapper
2022-06-06 02:54:04 +08:00
func ( v * Volume ) readNeedle ( n * needle . Needle , readOption * ReadOption , onReadSizeFn func ( size Size ) ) ( count int , err error ) {
2021-03-22 04:05:53 +08:00
v . dataFileAccessLock . RLock ( )
defer v . dataFileAccessLock . RUnlock ( )
nv , ok := v . nm . Get ( n . Id )
if ! ok || nv . Offset . IsZero ( ) {
return - 1 , ErrorNotFound
}
readSize := nv . Size
if readSize . IsDeleted ( ) {
if readOption != nil && readOption . ReadDeleted && readSize != TombstoneFileSize {
glog . V ( 3 ) . Infof ( "reading deleted %s" , n . String ( ) )
readSize = - readSize
} else {
return - 1 , ErrorDeleted
}
}
if readSize == 0 {
return 0 , nil
}
2021-08-09 14:25:16 +08:00
if onReadSizeFn != nil {
onReadSizeFn ( readSize )
}
2022-06-06 02:54:04 +08:00
if readOption != nil && readOption . AttemptMetaOnly && readSize > PagedReadLimit {
readOption . VolumeRevision = v . SuperBlock . CompactionRevision
2022-06-06 06:24:02 +08:00
err = n . ReadNeedleMeta ( v . DataBackend , nv . Offset . ToActualOffset ( ) , readSize , v . Version ( ) )
2022-06-06 02:54:04 +08:00
if err == needle . ErrorSizeMismatch && OffsetSize == 4 {
readOption . IsOutOfRange = true
2022-06-06 06:24:02 +08:00
err = n . ReadNeedleMeta ( v . DataBackend , nv . Offset . ToActualOffset ( ) + int64 ( MaxPossibleVolumeSize ) , readSize , v . Version ( ) )
2022-06-06 02:54:04 +08:00
}
if err != nil {
return 0 , err
}
if ! n . IsCompressed ( ) && ! n . IsChunkedManifest ( ) {
readOption . IsMetaOnly = true
}
2021-03-22 04:05:53 +08:00
}
2022-06-06 02:54:04 +08:00
if readOption == nil || ! readOption . IsMetaOnly {
err = n . ReadData ( v . DataBackend , nv . Offset . ToActualOffset ( ) , readSize , v . Version ( ) )
if err == needle . ErrorSizeMismatch && OffsetSize == 4 {
err = n . ReadData ( v . DataBackend , nv . Offset . ToActualOffset ( ) + int64 ( MaxPossibleVolumeSize ) , readSize , v . Version ( ) )
}
v . checkReadWriteError ( err )
if err != nil {
return 0 , err
}
2021-03-22 04:05:53 +08:00
}
2022-06-06 02:54:04 +08:00
count = int ( n . DataSize )
2021-03-22 04:05:53 +08:00
if ! n . HasTtl ( ) {
2022-06-06 02:54:04 +08:00
return
2021-03-22 04:05:53 +08:00
}
ttlMinutes := n . Ttl . Minutes ( )
if ttlMinutes == 0 {
2022-06-06 02:54:04 +08:00
return
2021-03-22 04:05:53 +08:00
}
if ! n . HasLastModifiedDate ( ) {
2022-06-06 02:54:04 +08:00
return
2021-03-22 04:05:53 +08:00
}
if time . Now ( ) . Before ( time . Unix ( 0 , int64 ( n . AppendAtNs ) ) . Add ( time . Duration ( ttlMinutes ) * time . Minute ) ) {
2022-06-06 02:54:04 +08:00
return
2021-03-22 04:05:53 +08:00
}
return - 1 , ErrorNotFound
}
2022-06-06 02:54:04 +08:00
// read fills in Needle content by looking up n.Id from NeedleMapper
func ( v * Volume ) readNeedleDataInto ( n * needle . Needle , readOption * ReadOption , writer io . Writer , offset int64 , size int64 ) ( err error ) {
v . dataFileAccessLock . RLock ( )
defer v . dataFileAccessLock . RUnlock ( )
nv , ok := v . nm . Get ( n . Id )
if ! ok || nv . Offset . IsZero ( ) {
return ErrorNotFound
}
readSize := nv . Size
if readSize . IsDeleted ( ) {
if readOption != nil && readOption . ReadDeleted && readSize != TombstoneFileSize {
glog . V ( 3 ) . Infof ( "reading deleted %s" , n . String ( ) )
readSize = - readSize
} else {
return ErrorDeleted
}
}
if readSize == 0 {
return nil
}
if readOption . VolumeRevision != v . SuperBlock . CompactionRevision {
// the volume is compacted
readOption . IsOutOfRange = false
2022-06-06 06:24:02 +08:00
err = n . ReadNeedleMeta ( v . DataBackend , nv . Offset . ToActualOffset ( ) , readSize , v . Version ( ) )
2022-06-06 02:54:04 +08:00
}
buf := mem . Allocate ( 1024 * 1024 )
defer mem . Free ( buf )
actualOffset := nv . Offset . ToActualOffset ( )
if readOption . IsOutOfRange {
actualOffset += int64 ( MaxPossibleVolumeSize )
}
2022-06-06 06:24:02 +08:00
return n . ReadNeedleDataInto ( v . DataBackend , actualOffset , buf , writer , offset , size )
2022-06-06 02:54:04 +08:00
}
2021-03-22 15:03:16 +08:00
// read fills in Needle content by looking up n.Id from NeedleMapper
func ( v * Volume ) ReadNeedleBlob ( offset int64 , size Size ) ( [ ] byte , error ) {
v . dataFileAccessLock . RLock ( )
defer v . dataFileAccessLock . RUnlock ( )
return needle . ReadNeedleBlob ( v . DataBackend , offset , size , v . Version ( ) )
}
2021-03-22 04:05:53 +08:00
type VolumeFileScanner interface {
VisitSuperBlock ( super_block . SuperBlock ) error
ReadNeedleBody ( ) bool
VisitNeedle ( n * needle . Needle , offset int64 , needleHeader , needleBody [ ] byte ) error
}
func ScanVolumeFile ( dirname string , collection string , id needle . VolumeId ,
needleMapKind NeedleMapKind ,
volumeFileScanner VolumeFileScanner ) ( err error ) {
var v * Volume
if v , err = loadVolumeWithoutIndex ( dirname , collection , id , needleMapKind ) ; err != nil {
return fmt . Errorf ( "failed to load volume %d: %v" , id , err )
}
if err = volumeFileScanner . VisitSuperBlock ( v . SuperBlock ) ; err != nil {
return fmt . Errorf ( "failed to process volume %d super block: %v" , id , err )
}
defer v . Close ( )
version := v . Version ( )
offset := int64 ( v . SuperBlock . BlockSize ( ) )
return ScanVolumeFileFrom ( version , v . DataBackend , offset , volumeFileScanner )
}
func ScanVolumeFileFrom ( version needle . Version , datBackend backend . BackendStorageFile , offset int64 , volumeFileScanner VolumeFileScanner ) ( err error ) {
n , nh , rest , e := needle . ReadNeedleHeader ( datBackend , version , offset )
if e != nil {
if e == io . EOF {
return nil
}
return fmt . Errorf ( "cannot read %s at offset %d: %v" , datBackend . Name ( ) , offset , e )
}
for n != nil {
var needleBody [ ] byte
if volumeFileScanner . ReadNeedleBody ( ) {
// println("needle", n.Id.String(), "offset", offset, "size", n.Size, "rest", rest)
if needleBody , err = n . ReadNeedleBody ( datBackend , version , offset + NeedleHeaderSize , rest ) ; err != nil {
glog . V ( 0 ) . Infof ( "cannot read needle head [%d, %d) body [%d, %d) body length %d: %v" , offset , offset + NeedleHeaderSize , offset + NeedleHeaderSize , offset + NeedleHeaderSize + rest , rest , err )
// err = fmt.Errorf("cannot read needle body: %v", err)
// return
}
}
err := volumeFileScanner . VisitNeedle ( n , offset , nh , needleBody )
if err == io . EOF {
return nil
}
if err != nil {
glog . V ( 0 ) . Infof ( "visit needle error: %v" , err )
return fmt . Errorf ( "visit needle error: %v" , err )
}
offset += NeedleHeaderSize + rest
glog . V ( 4 ) . Infof ( "==> new entry offset %d" , offset )
if n , nh , rest , err = needle . ReadNeedleHeader ( datBackend , version , offset ) ; err != nil {
if err == io . EOF {
return nil
}
return fmt . Errorf ( "cannot read needle header at offset %d: %v" , offset , err )
}
glog . V ( 4 ) . Infof ( "new entry needle size:%d rest:%d" , n . Size , rest )
}
return nil
}