mirror of
https://github.com/cubefs/cubefs.git
synced 2026-08-02 02:00:56 +00:00
686 lines
18 KiB
Go
686 lines
18 KiB
Go
// Copyright 2018 The CubeFS Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
// implied. See the License for the specific language governing
|
|
// permissions and limitations under the License.
|
|
|
|
package stream
|
|
|
|
import (
|
|
"fmt"
|
|
"hash/crc32"
|
|
"net"
|
|
"sync/atomic"
|
|
"syscall"
|
|
"time"
|
|
|
|
"golang.org/x/net/context"
|
|
|
|
"github.com/cubefs/cubefs/proto"
|
|
"github.com/cubefs/cubefs/sdk/data/wrapper"
|
|
"github.com/cubefs/cubefs/storage"
|
|
"github.com/cubefs/cubefs/util"
|
|
"github.com/cubefs/cubefs/util/errors"
|
|
"github.com/cubefs/cubefs/util/log"
|
|
)
|
|
|
|
const (
|
|
MaxSelectDataPartitionForWrite = 32
|
|
MaxNewHandlerRetry = 3
|
|
MaxPacketErrorCount = 128
|
|
MaxDirtyListLen = 0
|
|
)
|
|
|
|
const (
|
|
StreamerNormal int32 = iota
|
|
StreamerError
|
|
)
|
|
|
|
const (
|
|
streamWriterFlushPeriod = 3
|
|
streamWriterIdleTimeoutPeriod = 10
|
|
)
|
|
|
|
// OpenRequest defines an open request.
|
|
type OpenRequest struct {
|
|
done chan struct{}
|
|
}
|
|
|
|
// WriteRequest defines a write request.
|
|
type WriteRequest struct {
|
|
fileOffset int
|
|
size int
|
|
data []byte
|
|
flags int
|
|
writeBytes int
|
|
err error
|
|
done chan struct{}
|
|
}
|
|
|
|
// FlushRequest defines a flush request.
|
|
type FlushRequest struct {
|
|
err error
|
|
done chan struct{}
|
|
}
|
|
|
|
// ReleaseRequest defines a release request.
|
|
type ReleaseRequest struct {
|
|
err error
|
|
done chan struct{}
|
|
}
|
|
|
|
// TruncRequest defines a truncate request.
|
|
type TruncRequest struct {
|
|
size int
|
|
err error
|
|
done chan struct{}
|
|
}
|
|
|
|
// EvictRequest defines an evict request.
|
|
type EvictRequest struct {
|
|
err error
|
|
done chan struct{}
|
|
}
|
|
|
|
// Open request shall grab the lock until request is sent to the request channel
|
|
func (s *Streamer) IssueOpenRequest() error {
|
|
request := openRequestPool.Get().(*OpenRequest)
|
|
request.done = make(chan struct{}, 1)
|
|
s.request <- request
|
|
s.client.streamerLock.Unlock()
|
|
<-request.done
|
|
openRequestPool.Put(request)
|
|
return nil
|
|
}
|
|
|
|
func (s *Streamer) IssueWriteRequest(offset int, data []byte, flags int) (write int, err error) {
|
|
if atomic.LoadInt32(&s.status) >= StreamerError {
|
|
return 0, errors.New(fmt.Sprintf("IssueWriteRequest: stream writer in error status, ino(%v)", s.inode))
|
|
}
|
|
|
|
s.writeLock.Lock()
|
|
request := writeRequestPool.Get().(*WriteRequest)
|
|
request.data = data
|
|
request.fileOffset = offset
|
|
request.size = len(data)
|
|
request.flags = flags
|
|
request.done = make(chan struct{}, 1)
|
|
s.request <- request
|
|
s.writeLock.Unlock()
|
|
|
|
<-request.done
|
|
err = request.err
|
|
write = request.writeBytes
|
|
writeRequestPool.Put(request)
|
|
return
|
|
}
|
|
|
|
func (s *Streamer) IssueFlushRequest() error {
|
|
request := flushRequestPool.Get().(*FlushRequest)
|
|
request.done = make(chan struct{}, 1)
|
|
s.request <- request
|
|
<-request.done
|
|
err := request.err
|
|
flushRequestPool.Put(request)
|
|
return err
|
|
}
|
|
|
|
func (s *Streamer) IssueReleaseRequest() error {
|
|
request := releaseRequestPool.Get().(*ReleaseRequest)
|
|
request.done = make(chan struct{}, 1)
|
|
s.request <- request
|
|
s.client.streamerLock.Unlock()
|
|
<-request.done
|
|
err := request.err
|
|
releaseRequestPool.Put(request)
|
|
return err
|
|
}
|
|
|
|
func (s *Streamer) IssueTruncRequest(size int) error {
|
|
request := truncRequestPool.Get().(*TruncRequest)
|
|
request.size = size
|
|
request.done = make(chan struct{}, 1)
|
|
s.request <- request
|
|
<-request.done
|
|
err := request.err
|
|
truncRequestPool.Put(request)
|
|
return err
|
|
}
|
|
|
|
func (s *Streamer) IssueEvictRequest() error {
|
|
request := evictRequestPool.Get().(*EvictRequest)
|
|
request.done = make(chan struct{}, 1)
|
|
s.request <- request
|
|
s.client.streamerLock.Unlock()
|
|
<-request.done
|
|
err := request.err
|
|
evictRequestPool.Put(request)
|
|
return err
|
|
}
|
|
|
|
func (s *Streamer) server() {
|
|
t := time.NewTicker(2 * time.Second)
|
|
defer t.Stop()
|
|
//defer func() {
|
|
// if !s.client.disableMetaCache && s.needBCache {
|
|
// close(s.request)
|
|
// s.request = nil
|
|
// }
|
|
//}()
|
|
|
|
for {
|
|
select {
|
|
case request := <-s.request:
|
|
s.handleRequest(request)
|
|
s.idle = 0
|
|
s.traversed = 0
|
|
case <-s.done:
|
|
s.abort()
|
|
log.LogDebugf("done server: evict, ino(%v)", s.inode)
|
|
return
|
|
case <-t.C:
|
|
s.traverse()
|
|
if s.refcnt <= 0 {
|
|
|
|
s.client.streamerLock.Lock()
|
|
if s.idle >= streamWriterIdleTimeoutPeriod && len(s.request) == 0 {
|
|
if s.client.disableMetaCache || !s.needBCache {
|
|
delete(s.client.streamers, s.inode)
|
|
if s.client.evictIcache != nil {
|
|
s.client.evictIcache(s.inode)
|
|
}
|
|
}
|
|
|
|
s.isOpen = false
|
|
// fail the remaining requests in such case
|
|
s.clearRequests()
|
|
s.client.streamerLock.Unlock()
|
|
|
|
log.LogDebugf("done server: no requests for a long time, ino(%v)", s.inode)
|
|
return
|
|
}
|
|
s.client.streamerLock.Unlock()
|
|
|
|
s.idle++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Streamer) clearRequests() {
|
|
for {
|
|
select {
|
|
case request := <-s.request:
|
|
s.abortRequest(request)
|
|
default:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Streamer) abortRequest(request interface{}) {
|
|
switch request := request.(type) {
|
|
case *OpenRequest:
|
|
request.done <- struct{}{}
|
|
case *WriteRequest:
|
|
request.err = syscall.EAGAIN
|
|
request.done <- struct{}{}
|
|
case *TruncRequest:
|
|
request.err = syscall.EAGAIN
|
|
request.done <- struct{}{}
|
|
case *FlushRequest:
|
|
request.err = syscall.EAGAIN
|
|
request.done <- struct{}{}
|
|
case *ReleaseRequest:
|
|
request.err = syscall.EAGAIN
|
|
request.done <- struct{}{}
|
|
case *EvictRequest:
|
|
request.err = syscall.EAGAIN
|
|
request.done <- struct{}{}
|
|
default:
|
|
}
|
|
}
|
|
|
|
func (s *Streamer) handleRequest(request interface{}) {
|
|
switch request := request.(type) {
|
|
case *OpenRequest:
|
|
s.open()
|
|
request.done <- struct{}{}
|
|
case *WriteRequest:
|
|
request.writeBytes, request.err = s.write(request.data, request.fileOffset, request.size, request.flags)
|
|
request.done <- struct{}{}
|
|
case *TruncRequest:
|
|
request.err = s.truncate(request.size)
|
|
request.done <- struct{}{}
|
|
case *FlushRequest:
|
|
request.err = s.flush()
|
|
request.done <- struct{}{}
|
|
case *ReleaseRequest:
|
|
request.err = s.release()
|
|
request.done <- struct{}{}
|
|
case *EvictRequest:
|
|
request.err = s.evict()
|
|
request.done <- struct{}{}
|
|
default:
|
|
}
|
|
}
|
|
|
|
func (s *Streamer) write(data []byte, offset, size, flags int) (total int, err error) {
|
|
var direct bool
|
|
|
|
if flags&proto.FlagsSyncWrite != 0 {
|
|
direct = true
|
|
}
|
|
|
|
if flags&proto.FlagsAppend != 0 {
|
|
filesize, _ := s.extents.Size()
|
|
offset = filesize
|
|
}
|
|
|
|
log.LogDebugf("Streamer write enter: ino(%v) offset(%v) size(%v)", s.inode, offset, size)
|
|
|
|
ctx := context.Background()
|
|
s.client.writeLimiter.Wait(ctx)
|
|
|
|
requests := s.extents.PrepareWriteRequests(offset, size, data)
|
|
log.LogDebugf("Streamer write: ino(%v) prepared requests(%v)", s.inode, requests)
|
|
|
|
// Must flush before doing overwrite
|
|
for _, req := range requests {
|
|
if req.ExtentKey == nil {
|
|
continue
|
|
}
|
|
err = s.flush()
|
|
if err != nil {
|
|
return
|
|
}
|
|
requests = s.extents.PrepareWriteRequests(offset, size, data)
|
|
log.LogDebugf("Streamer write: ino(%v) prepared requests after flush(%v)", s.inode, requests)
|
|
break
|
|
}
|
|
|
|
for _, req := range requests {
|
|
var writeSize int
|
|
if req.ExtentKey != nil {
|
|
writeSize, err = s.doOverwrite(req, direct)
|
|
if s.client.bcacheEnable {
|
|
cacheKey := util.GenerateRepVolKey(s.client.volumeName, s.inode, req.ExtentKey.PartitionId, req.ExtentKey.ExtentId, uint64(req.FileOffset))
|
|
if _, ok := s.inflightEvictL1cache.Load(cacheKey); !ok {
|
|
go func(cacheKey string) {
|
|
s.inflightEvictL1cache.Store(cacheKey, true)
|
|
s.client.evictBcache(cacheKey)
|
|
s.inflightEvictL1cache.Delete(cacheKey)
|
|
}(cacheKey)
|
|
}
|
|
}
|
|
|
|
} else {
|
|
writeSize, err = s.doWrite(req.Data, req.FileOffset, req.Size, direct)
|
|
}
|
|
if err != nil {
|
|
log.LogErrorf("Streamer write: ino(%v) err(%v)", s.inode, err)
|
|
break
|
|
}
|
|
total += writeSize
|
|
}
|
|
if filesize, _ := s.extents.Size(); offset+total > filesize {
|
|
s.extents.SetSize(uint64(offset+total), false)
|
|
log.LogDebugf("Streamer write: ino(%v) filesize changed to (%v)", s.inode, offset+total)
|
|
}
|
|
log.LogDebugf("Streamer write exit: ino(%v) offset(%v) size(%v) done total(%v) err(%v)", s.inode, offset, size, total, err)
|
|
return
|
|
}
|
|
|
|
func (s *Streamer) doOverwrite(req *ExtentRequest, direct bool) (total int, err error) {
|
|
var dp *wrapper.DataPartition
|
|
|
|
err = s.flush()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
offset := req.FileOffset
|
|
size := req.Size
|
|
|
|
// the extent key needs to be updated because when preparing the requests,
|
|
// the obtained extent key could be a local key which can be inconsistent with the remote key.
|
|
req.ExtentKey = s.extents.Get(uint64(offset))
|
|
ekFileOffset := int(req.ExtentKey.FileOffset)
|
|
ekExtOffset := int(req.ExtentKey.ExtentOffset)
|
|
if req.ExtentKey == nil {
|
|
err = errors.New(fmt.Sprintf("doOverwrite: extent key not exist, ino(%v) ekFileOffset(%v) ek(%v)", s.inode, ekFileOffset, req.ExtentKey))
|
|
return
|
|
}
|
|
|
|
if dp, err = s.client.dataWrapper.GetDataPartition(req.ExtentKey.PartitionId); err != nil {
|
|
// TODO unhandled error
|
|
errors.Trace(err, "doOverwrite: ino(%v) failed to get datapartition, ek(%v)", s.inode, req.ExtentKey)
|
|
return
|
|
}
|
|
|
|
retry := true
|
|
if proto.IsCold(s.client.volumeType) {
|
|
retry = false
|
|
}
|
|
|
|
sc := NewStreamConn(dp, false)
|
|
|
|
for total < size {
|
|
reqPacket := NewOverwritePacket(dp, req.ExtentKey.ExtentId, offset-ekFileOffset+total+ekExtOffset, s.inode, offset)
|
|
if direct {
|
|
reqPacket.Opcode = proto.OpSyncRandomWrite
|
|
}
|
|
packSize := util.Min(size-total, util.BlockSize)
|
|
copy(reqPacket.Data[:packSize], req.Data[total:total+packSize])
|
|
reqPacket.Size = uint32(packSize)
|
|
reqPacket.CRC = crc32.ChecksumIEEE(reqPacket.Data[:packSize])
|
|
|
|
replyPacket := new(Packet)
|
|
err = sc.Send(retry, reqPacket, func(conn *net.TCPConn) (error, bool) {
|
|
e := replyPacket.ReadFromConn(conn, proto.ReadDeadlineTime)
|
|
if e != nil {
|
|
log.LogWarnf("Stream Writer doOverwrite: ino(%v) failed to read from connect, req(%v) err(%v)", s.inode, reqPacket, e)
|
|
// Upon receiving TryOtherAddrError, other hosts will be retried.
|
|
return TryOtherAddrError, false
|
|
}
|
|
|
|
if replyPacket.ResultCode == proto.OpAgain {
|
|
return nil, true
|
|
}
|
|
|
|
if replyPacket.ResultCode == proto.OpTryOtherAddr {
|
|
e = TryOtherAddrError
|
|
}
|
|
return e, false
|
|
})
|
|
|
|
proto.Buffers.Put(reqPacket.Data)
|
|
reqPacket.Data = nil
|
|
log.LogDebugf("doOverwrite: ino(%v) req(%v) reqPacket(%v) err(%v) replyPacket(%v)", s.inode, req, reqPacket, err, replyPacket)
|
|
|
|
if err != nil || replyPacket.ResultCode != proto.OpOk {
|
|
err = errors.New(fmt.Sprintf("doOverwrite: failed or reply NOK: err(%v) ino(%v) req(%v) replyPacket(%v)", err, s.inode, req, replyPacket))
|
|
break
|
|
}
|
|
|
|
if !reqPacket.isValidWriteReply(replyPacket) || reqPacket.CRC != replyPacket.CRC {
|
|
err = errors.New(fmt.Sprintf("doOverwrite: is not the corresponding reply, ino(%v) req(%v) replyPacket(%v)", s.inode, req, replyPacket))
|
|
break
|
|
}
|
|
|
|
total += packSize
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s *Streamer) doWrite(data []byte, offset, size int, direct bool) (total int, err error) {
|
|
var (
|
|
ek *proto.ExtentKey
|
|
storeMode int
|
|
)
|
|
|
|
// Small files are usually written in a single write, so use tiny extent
|
|
// store only for the first write operation.
|
|
if offset > 0 || offset+size > s.tinySizeLimit() {
|
|
storeMode = proto.NormalExtentType
|
|
} else {
|
|
storeMode = proto.TinyExtentType
|
|
}
|
|
|
|
log.LogDebugf("doWrite enter: ino(%v) offset(%v) size(%v) storeMode(%v)", s.inode, offset, size, storeMode)
|
|
if proto.IsHot(s.client.volumeType) {
|
|
if storeMode == proto.NormalExtentType && (s.handler == nil || s.handler != nil && s.handler.fileOffset+s.handler.size != offset) {
|
|
if currentEK := s.extents.GetEnd(uint64(offset)); currentEK != nil && !storage.IsTinyExtent(currentEK.ExtentId) {
|
|
s.closeOpenHandler()
|
|
|
|
log.LogDebugf("doWrite: found ek in ExtentCache, offset(%v) size(%v), ekoffset(%v) eksize(%v)",
|
|
offset, size, currentEK.FileOffset, currentEK.Size)
|
|
_, pidErr := s.client.dataWrapper.GetDataPartition(currentEK.PartitionId)
|
|
if pidErr == nil {
|
|
handler := NewExtentHandler(s, int(currentEK.FileOffset), storeMode, int(currentEK.Size))
|
|
handler.key = &proto.ExtentKey{
|
|
FileOffset: currentEK.FileOffset,
|
|
PartitionId: currentEK.PartitionId,
|
|
ExtentId: currentEK.ExtentId,
|
|
ExtentOffset: currentEK.ExtentOffset,
|
|
Size: currentEK.Size,
|
|
}
|
|
s.handler = handler
|
|
s.dirty = false
|
|
log.LogDebugf("doWrite: currentEK.PartitionId(%v) found", currentEK.PartitionId)
|
|
} else {
|
|
log.LogDebugf("doWrite: currentEK.PartitionId(%v) not found", currentEK.PartitionId)
|
|
}
|
|
|
|
} else {
|
|
log.LogDebugf("doWrite: not found ek in ExtentCache, offset(%v) size(%v)", offset, size)
|
|
}
|
|
}
|
|
for i := 0; i < MaxNewHandlerRetry; i++ {
|
|
if s.handler == nil {
|
|
s.handler = NewExtentHandler(s, offset, storeMode, 0)
|
|
s.dirty = false
|
|
} else if s.handler.storeMode != storeMode {
|
|
// store mode changed, so close open handler and start a new one
|
|
s.closeOpenHandler()
|
|
continue
|
|
}
|
|
ek, err = s.handler.write(data, offset, size, direct)
|
|
if err == nil && ek != nil {
|
|
if !s.dirty {
|
|
s.dirtylist.Put(s.handler)
|
|
s.dirty = true
|
|
}
|
|
break
|
|
}
|
|
|
|
log.LogDebugf("doWrite handler write failed so close open handler: ino(%v) offset(%v) size(%v) storeMode(%v) err(%v)",
|
|
s.inode, offset, size, storeMode, err)
|
|
|
|
s.closeOpenHandler()
|
|
}
|
|
} else {
|
|
s.handler = NewExtentHandler(s, offset, storeMode, 0)
|
|
s.dirty = false
|
|
ek, err = s.handler.write(data, offset, size, direct)
|
|
if err == nil && ek != nil {
|
|
if !s.dirty {
|
|
s.dirtylist.Put(s.handler)
|
|
s.dirty = true
|
|
}
|
|
}
|
|
|
|
log.LogDebugf("doWrite handler write failed so close open handler: ino(%v) offset(%v) size(%v) storeMode(%v) err(%v)",
|
|
s.inode, offset, size, storeMode, err)
|
|
err = s.closeOpenHandler()
|
|
}
|
|
|
|
if err != nil || ek == nil {
|
|
log.LogErrorf("doWrite error: ino(%v) offset(%v) size(%v) err(%v) ek(%v)", s.inode, offset, size, err, ek)
|
|
return
|
|
}
|
|
|
|
// This ek is just a local cache for PrepareWriteRequest, so ignore discard eks here.
|
|
_ = s.extents.Append(ek, false)
|
|
total = size
|
|
|
|
log.LogDebugf("doWrite exit: ino(%v) offset(%v) size(%v) ek(%v)", s.inode, offset, size, ek)
|
|
return
|
|
}
|
|
|
|
func (s *Streamer) flush() (err error) {
|
|
for {
|
|
element := s.dirtylist.Get()
|
|
if element == nil {
|
|
break
|
|
}
|
|
eh := element.Value.(*ExtentHandler)
|
|
|
|
log.LogDebugf("Streamer flush begin: eh(%v)", eh)
|
|
err = eh.flush()
|
|
if err != nil {
|
|
log.LogErrorf("Streamer flush failed: eh(%v)", eh)
|
|
return
|
|
}
|
|
eh.stream.dirtylist.Remove(element)
|
|
if eh.getStatus() == ExtentStatusOpen {
|
|
s.dirty = false
|
|
log.LogDebugf("Streamer flush handler open: eh(%v)", eh)
|
|
} else {
|
|
// TODO unhandled error
|
|
eh.cleanup()
|
|
log.LogDebugf("Streamer flush handler cleaned up: eh(%v)", eh)
|
|
}
|
|
log.LogDebugf("Streamer flush end: eh(%v)", eh)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s *Streamer) traverse() (err error) {
|
|
s.traversed++
|
|
length := s.dirtylist.Len()
|
|
for i := 0; i < length; i++ {
|
|
element := s.dirtylist.Get()
|
|
if element == nil {
|
|
break
|
|
}
|
|
eh := element.Value.(*ExtentHandler)
|
|
|
|
log.LogDebugf("Streamer traverse begin: eh(%v)", eh)
|
|
if eh.getStatus() >= ExtentStatusClosed {
|
|
// handler can be in different status such as close, recovery, and error,
|
|
// and therefore there can be packet that has not been flushed yet.
|
|
eh.flushPacket()
|
|
if atomic.LoadInt32(&eh.inflight) > 0 {
|
|
log.LogDebugf("Streamer traverse skipped: non-zero inflight, eh(%v)", eh)
|
|
continue
|
|
}
|
|
err = eh.appendExtentKey()
|
|
if err != nil {
|
|
log.LogWarnf("Streamer traverse abort: appendExtentKey failed, eh(%v) err(%v)", eh, err)
|
|
// set the streamer to error status to avoid further writes
|
|
if err == syscall.EIO {
|
|
atomic.StoreInt32(&eh.stream.status, StreamerError)
|
|
}
|
|
return
|
|
}
|
|
s.dirtylist.Remove(element)
|
|
eh.cleanup()
|
|
} else {
|
|
if s.traversed < streamWriterFlushPeriod {
|
|
log.LogDebugf("Streamer traverse skipped: traversed(%v) eh(%v)", s.traversed, eh)
|
|
continue
|
|
}
|
|
if err = eh.flush(); err != nil {
|
|
log.LogWarnf("Streamer traverse flush: eh(%v) err(%v)", eh, err)
|
|
}
|
|
}
|
|
log.LogDebugf("Streamer traverse end: eh(%v)", eh)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s *Streamer) closeOpenHandler() (err error) {
|
|
// just in case to avoid infinite loop
|
|
var cnt int = 2 * MaxPacketErrorCount
|
|
|
|
handler := s.handler
|
|
for handler != nil && cnt >= 0 {
|
|
handler.setClosed()
|
|
if s.dirtylist.Len() < MaxDirtyListLen {
|
|
handler.flushPacket()
|
|
} else {
|
|
// TODO unhandled error
|
|
err = s.handler.flush()
|
|
}
|
|
handler = handler.recoverHandler
|
|
cnt--
|
|
}
|
|
|
|
if s.handler != nil {
|
|
if !s.dirty {
|
|
// in case the current handler is not on the dirty list and will not get cleaned up
|
|
// TODO unhandled error
|
|
s.handler.cleanup()
|
|
}
|
|
s.handler = nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (s *Streamer) open() {
|
|
s.refcnt++
|
|
log.LogDebugf("open: streamer(%v) refcnt(%v)", s, s.refcnt)
|
|
}
|
|
|
|
func (s *Streamer) release() error {
|
|
s.refcnt--
|
|
s.closeOpenHandler()
|
|
err := s.flush()
|
|
if err != nil {
|
|
s.abort()
|
|
}
|
|
log.LogDebugf("release: streamer(%v) refcnt(%v)", s, s.refcnt)
|
|
return err
|
|
}
|
|
|
|
func (s *Streamer) evict() error {
|
|
s.client.streamerLock.Lock()
|
|
if s.refcnt > 0 || len(s.request) != 0 {
|
|
s.client.streamerLock.Unlock()
|
|
return errors.New(fmt.Sprintf("evict: streamer(%v) refcnt(%v)", s, s.refcnt))
|
|
}
|
|
if s.client.disableMetaCache || !s.needBCache {
|
|
delete(s.client.streamers, s.inode)
|
|
}
|
|
s.client.streamerLock.Unlock()
|
|
return nil
|
|
}
|
|
|
|
func (s *Streamer) abort() {
|
|
for {
|
|
element := s.dirtylist.Get()
|
|
if element == nil {
|
|
break
|
|
}
|
|
eh := element.Value.(*ExtentHandler)
|
|
s.dirtylist.Remove(element)
|
|
// TODO unhandled error
|
|
eh.cleanup()
|
|
}
|
|
}
|
|
|
|
func (s *Streamer) truncate(size int) error {
|
|
s.closeOpenHandler()
|
|
err := s.flush()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = s.client.truncate(s.inode, uint64(size))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
oldsize, _ := s.extents.Size()
|
|
if oldsize <= size {
|
|
s.extents.SetSize(uint64(size), true)
|
|
return nil
|
|
}
|
|
|
|
s.extents.TruncDiscard(uint64(size))
|
|
return s.GetExtentsForce()
|
|
}
|
|
|
|
func (s *Streamer) tinySizeLimit() int {
|
|
return util.DefaultTinySizeLimit
|
|
}
|