cubefs/flashnode/flashnode_op.go
slasher b150a10a36 feat(flashnode): add service of flashnode
. #22115361 #22200518 of #21957840

Signed-off-by: slasher <mcq.sejust@gmail.com>
2025-08-07 15:18:46 +08:00

304 lines
9.2 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 flashnode
import (
"context"
"fmt"
"net"
"runtime/debug"
"strings"
"sync"
"time"
"github.com/cubefs/cubefs/flashnode/cachengine"
"github.com/cubefs/cubefs/proto"
"github.com/cubefs/cubefs/util"
"github.com/cubefs/cubefs/util/errors"
"github.com/cubefs/cubefs/util/exporter"
"github.com/cubefs/cubefs/util/log"
)
func (f *FlashNode) preHandle(conn net.Conn, p *Packet) error {
if p.Opcode == proto.OpCachePrepare || p.Opcode == proto.OpCacheRead {
if f.cacheEngine == nil {
return errors.New("cache engine not started")
}
}
// request rate limit for entire flash node
if !(p.Opcode == proto.OpCacheRead && f.nodeLimit != 0) {
return nil
}
if !f.nodeLimiter.Allow() {
err := errors.NewErrorf("flashnode request is limited(%d)", f.nodeLimit)
metric := exporter.NewTPCnt("NodeReqLimit")
log.LogWarnf("action[preHandle] %s, remote address:%s", err.Error(), conn.RemoteAddr())
metric.Set(nil)
return err
}
return nil
}
// handlePacket handles the tcp packet operations.
func (f *FlashNode) handlePacket(conn net.Conn, p *Packet, remoteAddr string) (err error) {
metric := exporter.NewTPCnt(p.GetOpMsg())
defer func() {
metric.Set(err)
}()
switch p.Opcode {
case proto.OpFlashNodeHeartbeat:
err = f.opFlashNodeHeartbeat(conn, p, remoteAddr)
case proto.OpCacheRead:
err = f.opCacheRead(conn, p, remoteAddr)
case proto.OpCachePrepare:
err = f.opPrepare(conn, p, remoteAddr)
default:
err = fmt.Errorf("unknown Opcode:%d", p.Opcode)
}
if err != nil {
err = errors.NewErrorf("%s [%s] req: %d - %s", remoteAddr, p.GetOpMsg(), p.GetReqID(), err.Error())
}
return
}
func (f *FlashNode) opFlashNodeHeartbeat(conn net.Conn, p *Packet, remoteAddr string) (err error) {
p.PacketOkReply()
if err = p.WriteToConn(conn); err != nil {
log.LogErrorf("ack master response: %s", err.Error())
return err
}
log.LogInfof("%s [opMasterHeartbeat] ", remoteAddr)
return
}
func (f *FlashNode) opCacheRead(conn net.Conn, p *Packet, remoteAddr string) (err error) {
var (
block *cachengine.CacheBlock
req *proto.CacheReadRequest
volume string
)
defer func() {
if err != nil {
logContent := fmt.Sprintf("action[opCacheRead] volume:[%v], logMsg:%v.", volume,
p.LogMessage(p.GetOpMsg(), remoteAddr, p.StartT, err))
log.LogErrorf(logContent)
p.PacketErrorWithBody(proto.OpErr, ([]byte)(err.Error()))
_ = respondToClient(conn, p)
}
}()
ctx, ctxCancel := context.WithTimeout(context.Background(), proto.ReadCacheTimeout*time.Second*2)
defer ctxCancel()
if req, err = UnMarshalPacketToCacheRead(p); err != nil {
return
}
volume = req.CacheRequest.Volume
if !f.volLimitAllow(volume) {
err = errors.NewErrorf("volume(%s) request is limited(%d)", volume, f.volLimitMap[volume])
log.LogWarnf("action[preHandle] %s, remote address:%s", err.Error(), remoteAddr)
metric := exporter.NewTPCnt("VolReqLimit")
p.PacketErrorWithBody(proto.OpErr, ([]byte)(err.Error()))
_ = respondToClient(conn, p)
err = nil
metric.Set(nil)
return
}
if block, err = f.cacheEngine.GetCacheBlockForRead(volume, req.CacheRequest.Inode, req.CacheRequest.FixedFileOffset, req.CacheRequest.Version, req.Size_); err != nil {
if block, err = f.cacheEngine.CreateBlock(req.CacheRequest); err != nil {
return err
}
go block.InitOnce(f.cacheEngine, req.CacheRequest.Sources)
}
if err = f.doStreamReadRequest(ctx, conn, req, p, block); err != nil {
return
}
return
}
func (f *FlashNode) doStreamReadRequest(ctx context.Context, conn net.Conn, req *proto.CacheReadRequest, p *Packet, block *cachengine.CacheBlock) (err error) {
needReplySize := uint32(req.Size_)
offset := int64(req.Offset)
defer func() {
if err != nil {
err = fmt.Errorf("action[doStreamReadRequest] cache block(%v) err:%v", block.String(), err)
}
}()
for {
if needReplySize <= 0 {
break
}
err = nil
reply := NewCacheReply()
reply.ReqID = p.ReqID
reply.StartT = p.StartT
currReadSize := uint32(util.Min(int(needReplySize), util.ReadBlockSize))
if currReadSize == util.ReadBlockSize {
reply.Data, _ = proto.Buffers.Get(util.ReadBlockSize)
} else {
reply.Data = make([]byte, currReadSize)
}
reply.ExtentOffset = offset
p.Size = currReadSize
p.ExtentOffset = offset
err = func() error {
var storeErr error
reply.CRC, storeErr = block.Read(ctx, reply.Data[0:currReadSize], offset, int64(currReadSize))
return storeErr
}()
p.CRC = reply.CRC
if err != nil {
if currReadSize == util.ReadBlockSize {
proto.Buffers.Put(reply.Data)
}
return
}
reply.Size = currReadSize
reply.ResultCode = proto.OpOk
reply.Opcode = p.Opcode
p.ResultCode = proto.OpOk
err = func() error {
return reply.WriteToConn(conn)
}()
if err != nil {
if currReadSize == util.ReadBlockSize {
proto.Buffers.Put(reply.Data)
}
logContent := fmt.Sprintf("action[doStreamReadRequest] volume:[%v] %v.", req.CacheRequest.Volume,
reply.LogMessage(reply.GetOpMsg(), conn.RemoteAddr().String(), reply.StartT, err))
log.LogErrorf(logContent)
return
}
needReplySize -= currReadSize
offset += int64(currReadSize)
if currReadSize == util.ReadBlockSize {
proto.Buffers.Put(reply.Data)
}
logContent := fmt.Sprintf("action[doStreamReadRequest] ReqID[%v] volume:[%v] reply[%v] block[%v] .", p.ReqID, req.CacheRequest.Volume,
reply.LogMessage(reply.GetOpMsg(), conn.RemoteAddr().String(), reply.StartT, err), block.String())
log.LogReadf(logContent)
}
p.PacketOkReply()
return
}
func (f *FlashNode) opPrepare(conn net.Conn, p *Packet, remoteAddr string) (err error) {
var req *proto.CachePrepareRequest
var volume string
defer func() {
if err != nil {
logContent := fmt.Sprintf("action[opPrepare] volume:[%v] %v.", volume, p.LogMessage(p.GetOpMsg(), remoteAddr, p.StartT, err))
log.LogErrorf(logContent)
}
}()
reqID := p.ReqID
if req, err = UnMarshalPacketToCachePrepare(p); err != nil {
p.PacketErrorWithBody(proto.OpErr, ([]byte)(err.Error()))
_ = respondToClient(conn, p)
return err
}
p.PacketOkReply()
_ = respondToClient(conn, p)
volume = req.CacheRequest.Volume
if err = f.cacheEngine.PrepareCache(reqID, req.CacheRequest); err != nil {
return err
}
if len(req.FlashNodes) > 0 {
f.dispatchRequestToFollowers(req)
}
return
}
func (f *FlashNode) dispatchRequestToFollowers(request *proto.CachePrepareRequest) {
req := &proto.CachePrepareRequest{
CacheRequest: request.CacheRequest,
FlashNodes: make([]string, 0),
}
wg := sync.WaitGroup{}
for _, n := range request.FlashNodes {
if strings.Split(n, ":")[0] == f.localAddr {
continue
}
wg.Add(1)
go func(addr string) {
defer wg.Done()
if err := f.sendPrepareRequest(req, addr); err != nil {
log.LogErrorf("dispatchRequestToFollowers: failed to distribute request to addr(%v) err(%v)", addr, err)
}
}(n)
}
wg.Wait()
}
func (f *FlashNode) sendPrepareRequest(req *proto.CachePrepareRequest, target string) (err error) {
var conn *net.TCPConn
conn, err = f.connPool.GetConnect(target)
if err != nil {
return err
}
defer func() {
f.connPool.PutConnect(conn, err != nil)
}()
// TODO: xxx
// log.LogDebugf("action[sendPrepareRequest] request:%v", req)
// followerPacket, err := MarshalCachePrepareRequestToPacket(req)
// if err != nil {
// return err
// }
// if err = followerPacket.WriteToConnNs(conn, CacheReqWriteTimeoutMilliSec*1e6); err != nil {
// log.LogWarnf("action[sendPrepareRequest]: failed to write to addr(%v) err(%v)", target, err)
// return
// }
// replyPacket := common.NewCacheReply(followerPacket.Ctx())
// // replyPacket = repl.NewReplProtocol(followerPacket.Ctx())
// if err = replyPacket.ReadFromConnNs(conn, CacheReqReadTimeoutMilliSec*1e6); err != nil {
// log.LogWarnf("action[sendPrepareRequest]: failed to ReadFromConn, replyPacket(%v), fg host(%v), err(%v)", replyPacket, target, err)
// return
// }
// if replyPacket.ResultCode != proto.OpOk {
// log.LogWarnf("action[sendPrepareRequest]: ResultCode NOK, replyPacket(%v), fg host(%v), ResultCode(%v)", replyPacket, target, replyPacket.ResultCode)
// err = fmt.Errorf("ResultCode NOK (%v)", replyPacket.ResultCode)
// return
// }
return
}
// Reply data through tcp connection to the client.
func respondToClient(conn net.Conn, p *Packet) (err error) {
// Handle panic
defer func() {
if r := recover(); r != nil {
log.LogErrorf("respondToClient: panic occurred: %v\n%v", r, string(debug.Stack()))
switch data := r.(type) {
case error:
err = data
default:
err = errors.New(data.(string))
}
}
}()
// process data and send reply though specified tcp connection.
err = p.WriteToConn(conn)
if err != nil {
log.LogErrorf("response to client[%s], request[%s], response packet[%s]",
err.Error(), p.GetOpMsg(), p.GetResultMsg())
}
return
}