mirror of
https://github.com/cubefs/cubefs.git
synced 2026-08-02 02:00:56 +00:00
820 lines
18 KiB
Go
Executable File
820 lines
18 KiB
Go
Executable File
// 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.k
|
|
|
|
package proto
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/cubefs/cubefs/util/btree"
|
|
"github.com/cubefs/cubefs/util/log"
|
|
)
|
|
|
|
const (
|
|
DefaultTransactionTimeout = 1 // minutes
|
|
MaxTransactionTimeout = 60 // minutes
|
|
DefaultTxConflictRetryNum = 121
|
|
MaxTxConflictRetryNum = 1000
|
|
DefaultTxConflictRetryInterval = 500 // ms
|
|
MaxTxConflictRetryInterval = 1000 // ms
|
|
MinTxConflictRetryInterval = 10 // ms
|
|
DefaultTxDeleteTime = 120
|
|
ClearOrphanTxTime = 3600
|
|
DefaultAccessTimeValidInterval = 3600 * 24
|
|
MinAccessTimeValidInterval = 3600 * 12
|
|
MaxBufferSize = 1024 * 1024 * 1024 // 1GB
|
|
)
|
|
|
|
type TxOpMask uint8
|
|
|
|
const (
|
|
TxOpMaskOff TxOpMask = 0x00
|
|
TxOpMaskAll TxOpMask = 0x7F
|
|
TxPause TxOpMask = 0xFF
|
|
)
|
|
|
|
const (
|
|
TxOpMaskCreate TxOpMask = 0x01 << iota
|
|
TxOpMaskMkdir
|
|
TxOpMaskRemove
|
|
TxOpMaskRename
|
|
TxOpMaskMknod
|
|
TxOpMaskSymlink
|
|
TxOpMaskLink
|
|
)
|
|
|
|
var GTxMaskMap = map[string]TxOpMask{
|
|
"off": TxOpMaskOff,
|
|
"create": TxOpMaskCreate,
|
|
"mkdir": TxOpMaskMkdir,
|
|
"remove": TxOpMaskRemove,
|
|
"rename": TxOpMaskRename,
|
|
"mknod": TxOpMaskMknod,
|
|
"symlink": TxOpMaskSymlink,
|
|
"link": TxOpMaskLink,
|
|
"all": TxOpMaskAll,
|
|
}
|
|
|
|
func GetMaskString(mask TxOpMask) (maskStr string) {
|
|
if mask == TxPause {
|
|
return "pause"
|
|
}
|
|
|
|
if mask&TxOpMaskAll == TxOpMaskAll {
|
|
return "all"
|
|
}
|
|
|
|
for k, v := range GTxMaskMap {
|
|
if k == "all" {
|
|
continue
|
|
}
|
|
if mask&v > 0 {
|
|
if maskStr == "" {
|
|
maskStr = k
|
|
} else {
|
|
maskStr = maskStr + "|" + k
|
|
}
|
|
}
|
|
}
|
|
if maskStr == "" {
|
|
maskStr = "off"
|
|
}
|
|
return
|
|
}
|
|
|
|
func txInvalidMask() (err error) {
|
|
return errors.New("transaction mask key value pair should be: enableTxMaskKey=[create|mkdir|remove|rename|mknod|symlink|link]\n enableTxMaskKey=off \n enableTxMaskKey=all")
|
|
}
|
|
|
|
func MaskContains(mask TxOpMask, subMask TxOpMask) bool {
|
|
if mask != TxOpMaskOff && subMask == TxOpMaskOff {
|
|
return false
|
|
}
|
|
if (mask | subMask) != mask {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func GetMaskFromString(maskStr string) (mask TxOpMask, err error) {
|
|
if maskStr == "" {
|
|
err = txInvalidMask()
|
|
return
|
|
}
|
|
if maskStr == "pause" {
|
|
mask = TxPause
|
|
return
|
|
}
|
|
arr := strings.Split(maskStr, "|")
|
|
|
|
optNum := len(arr)
|
|
|
|
for _, v := range arr {
|
|
if m, ok := GTxMaskMap[v]; ok {
|
|
if optNum >= 2 && (m == TxOpMaskOff || m == TxOpMaskAll) {
|
|
mask = TxOpMaskOff
|
|
err = txInvalidMask()
|
|
return
|
|
} else {
|
|
mask = mask | m
|
|
}
|
|
} else {
|
|
mask = TxOpMaskOff
|
|
err = txInvalidMask()
|
|
return
|
|
}
|
|
}
|
|
return mask, nil
|
|
}
|
|
|
|
type TxInodeInfo struct {
|
|
Ino uint64
|
|
MpID uint64
|
|
CreateTime int64 // time.Now().Unix()
|
|
Timeout int64
|
|
TxID string
|
|
MpMembers string
|
|
}
|
|
|
|
func NewTxInodeInfo(members string, ino uint64, mpID uint64) *TxInodeInfo {
|
|
return &TxInodeInfo{
|
|
Ino: ino,
|
|
MpID: mpID,
|
|
MpMembers: members,
|
|
}
|
|
}
|
|
|
|
func (info *TxInodeInfo) String() string {
|
|
data, err := json.Marshal(info)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return string(data)
|
|
}
|
|
|
|
func (info *TxInodeInfo) Marshal() (result []byte, err error) {
|
|
buff := bytes.NewBuffer(make([]byte, 0, 128))
|
|
if err = binary.Write(buff, binary.BigEndian, &info.Ino); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = binary.Write(buff, binary.BigEndian, &info.MpID); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = binary.Write(buff, binary.BigEndian, &info.CreateTime); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = binary.Write(buff, binary.BigEndian, &info.Timeout); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
id := []byte(info.TxID)
|
|
idSize := uint32(len(id))
|
|
if err = binary.Write(buff, binary.BigEndian, &idSize); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err = buff.Write(id); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
addr := []byte(info.MpMembers)
|
|
addrSize := uint32(len(addr))
|
|
if err = binary.Write(buff, binary.BigEndian, &addrSize); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err = buff.Write(addr); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result = buff.Bytes()
|
|
return
|
|
}
|
|
|
|
func (info *TxInodeInfo) Unmarshal(raw []byte) (err error) {
|
|
buff := bytes.NewBuffer(raw)
|
|
if err = binary.Read(buff, binary.BigEndian, &info.Ino); err != nil {
|
|
return
|
|
}
|
|
if err = binary.Read(buff, binary.BigEndian, &info.MpID); err != nil {
|
|
return
|
|
}
|
|
if err = binary.Read(buff, binary.BigEndian, &info.CreateTime); err != nil {
|
|
return
|
|
}
|
|
if err = binary.Read(buff, binary.BigEndian, &info.Timeout); err != nil {
|
|
return
|
|
}
|
|
|
|
idSize := uint32(0)
|
|
if err = binary.Read(buff, binary.BigEndian, &idSize); err != nil {
|
|
return
|
|
}
|
|
if idSize > 0 {
|
|
if idSize > MaxBufferSize {
|
|
return ErrBufferSizeExceedMaximum
|
|
}
|
|
id := make([]byte, idSize)
|
|
if _, err = io.ReadFull(buff, id); err != nil {
|
|
return
|
|
}
|
|
info.TxID = string(id)
|
|
}
|
|
|
|
addrSize := uint32(0)
|
|
if err = binary.Read(buff, binary.BigEndian, &addrSize); err != nil {
|
|
return
|
|
}
|
|
if addrSize > 0 {
|
|
if addrSize > MaxBufferSize {
|
|
return ErrBufferSizeExceedMaximum
|
|
}
|
|
addr := make([]byte, addrSize)
|
|
if _, err = io.ReadFull(buff, addr); err != nil {
|
|
return
|
|
}
|
|
info.MpMembers = string(addr)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (info *TxInodeInfo) GetKey() uint64 {
|
|
return info.Ino
|
|
}
|
|
|
|
func (info *TxInodeInfo) SetTxId(txID string) {
|
|
info.TxID = txID
|
|
}
|
|
|
|
func (info *TxInodeInfo) SetTimeout(timeout int64) {
|
|
info.Timeout = timeout
|
|
}
|
|
|
|
func (info *TxInodeInfo) SetCreateTime(createTime int64) {
|
|
info.CreateTime = createTime
|
|
}
|
|
|
|
type TxDentryInfo struct {
|
|
ParentId uint64 // FileID value of the parent inode.
|
|
Name string // Name of the current dentry.
|
|
MpMembers string
|
|
TxID string
|
|
MpID uint64
|
|
CreateTime int64 // time.Now().Unix()
|
|
Timeout int64
|
|
}
|
|
|
|
func NewTxDentryInfo(members string, parentId uint64, name string, mpID uint64) *TxDentryInfo {
|
|
return &TxDentryInfo{
|
|
ParentId: parentId,
|
|
Name: name,
|
|
MpMembers: members,
|
|
MpID: mpID,
|
|
}
|
|
}
|
|
|
|
func (info *TxDentryInfo) String() string {
|
|
data, err := json.Marshal(info)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return string(data)
|
|
}
|
|
|
|
func (info *TxDentryInfo) Marshal() (result []byte, err error) {
|
|
buff := bytes.NewBuffer(make([]byte, 0, 128))
|
|
if err = binary.Write(buff, binary.BigEndian, &info.ParentId); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
name := []byte(info.Name)
|
|
nameSize := uint32(len(name))
|
|
if err = binary.Write(buff, binary.BigEndian, &nameSize); err != nil {
|
|
panic(err)
|
|
}
|
|
if _, err = buff.Write(name); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
addr := []byte(info.MpMembers)
|
|
addrSize := uint32(len(addr))
|
|
if err = binary.Write(buff, binary.BigEndian, &addrSize); err != nil {
|
|
panic(err)
|
|
}
|
|
if _, err = buff.Write(addr); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
id := []byte(info.TxID)
|
|
idSize := uint32(len(id))
|
|
if err = binary.Write(buff, binary.BigEndian, &idSize); err != nil {
|
|
panic(err)
|
|
}
|
|
if _, err = buff.Write(id); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err = binary.Write(buff, binary.BigEndian, &info.MpID); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err = binary.Write(buff, binary.BigEndian, &info.CreateTime); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err = binary.Write(buff, binary.BigEndian, &info.Timeout); err != nil {
|
|
panic(err)
|
|
}
|
|
result = buff.Bytes()
|
|
return
|
|
}
|
|
|
|
func (info *TxDentryInfo) Unmarshal(raw []byte) (err error) {
|
|
buff := bytes.NewBuffer(raw)
|
|
if err = binary.Read(buff, binary.BigEndian, &info.ParentId); err != nil {
|
|
return
|
|
}
|
|
|
|
nameSize := uint32(0)
|
|
if err = binary.Read(buff, binary.BigEndian, &nameSize); err != nil {
|
|
return
|
|
}
|
|
if nameSize > 0 {
|
|
if nameSize > MaxBufferSize {
|
|
return ErrBufferSizeExceedMaximum
|
|
}
|
|
name := make([]byte, nameSize)
|
|
if _, err = io.ReadFull(buff, name); err != nil {
|
|
return
|
|
}
|
|
info.Name = string(name)
|
|
}
|
|
|
|
addrSize := uint32(0)
|
|
if err = binary.Read(buff, binary.BigEndian, &addrSize); err != nil {
|
|
return
|
|
}
|
|
if addrSize > 0 {
|
|
if addrSize > MaxBufferSize {
|
|
return ErrBufferSizeExceedMaximum
|
|
}
|
|
addr := make([]byte, addrSize)
|
|
if _, err = io.ReadFull(buff, addr); err != nil {
|
|
return
|
|
}
|
|
info.MpMembers = string(addr)
|
|
}
|
|
|
|
idSize := uint32(0)
|
|
if err = binary.Read(buff, binary.BigEndian, &idSize); err != nil {
|
|
return
|
|
}
|
|
if idSize > 0 {
|
|
if idSize > MaxBufferSize {
|
|
return ErrBufferSizeExceedMaximum
|
|
}
|
|
id := make([]byte, idSize)
|
|
if _, err = io.ReadFull(buff, id); err != nil {
|
|
return
|
|
}
|
|
info.TxID = string(id)
|
|
}
|
|
|
|
if err = binary.Read(buff, binary.BigEndian, &info.MpID); err != nil {
|
|
return
|
|
}
|
|
|
|
if err = binary.Read(buff, binary.BigEndian, &info.CreateTime); err != nil {
|
|
return
|
|
}
|
|
|
|
if err = binary.Read(buff, binary.BigEndian, &info.Timeout); err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (info *TxDentryInfo) GetKey() string {
|
|
return strconv.FormatUint(info.ParentId, 10) + "_" + info.Name
|
|
}
|
|
|
|
func (info *TxDentryInfo) GetTxId() (string, error) {
|
|
if info.TxID == "" {
|
|
return "", errors.New("txID is not set")
|
|
}
|
|
return info.TxID, nil
|
|
}
|
|
|
|
func (info *TxDentryInfo) SetTxId(txID string) {
|
|
info.TxID = txID
|
|
}
|
|
|
|
func (info *TxDentryInfo) SetTimeout(timeout int64) {
|
|
info.Timeout = timeout
|
|
}
|
|
|
|
func (info *TxDentryInfo) SetCreateTime(createTime int64) {
|
|
info.CreateTime = createTime
|
|
}
|
|
|
|
const (
|
|
TxTypeUndefined uint32 = iota
|
|
TxTypeCreate
|
|
TxTypeMkdir
|
|
TxTypeRemove
|
|
TxTypeRename
|
|
TxTypeMknod
|
|
TxTypeSymlink
|
|
TxTypeLink
|
|
)
|
|
|
|
func TxMaskToType(mask TxOpMask) (txType uint32) {
|
|
switch mask {
|
|
case TxOpMaskOff:
|
|
txType = TxTypeUndefined
|
|
case TxOpMaskCreate:
|
|
txType = TxTypeCreate
|
|
case TxOpMaskMkdir:
|
|
txType = TxTypeMkdir
|
|
case TxOpMaskRemove:
|
|
txType = TxTypeRemove
|
|
case TxOpMaskRename:
|
|
txType = TxTypeRename
|
|
case TxOpMaskMknod:
|
|
txType = TxTypeMknod
|
|
case TxOpMaskSymlink:
|
|
txType = TxTypeSymlink
|
|
case TxOpMaskLink:
|
|
txType = TxTypeLink
|
|
default:
|
|
txType = TxTypeUndefined
|
|
}
|
|
return txType
|
|
}
|
|
|
|
const (
|
|
TxStateInit int32 = iota
|
|
TxStatePreCommit
|
|
TxStateCommit
|
|
TxStateRollback
|
|
TxStateCommitDone
|
|
TxStateRollbackDone
|
|
TxStateFailed
|
|
)
|
|
|
|
type TransactionInfo struct {
|
|
TxID string // "metapartitionId_atomicId", if empty, mp should be TM, otherwise it will be RM
|
|
TxType uint32
|
|
TmID int64
|
|
CreateTime int64 // time.Now()
|
|
Timeout int64 // minutes
|
|
State int32
|
|
DoneTime int64 // time.now()
|
|
RMFinish bool // used to check whether tx success on target rm.
|
|
// once insert to txTree, not change inode & dentry ifo
|
|
TxInodeInfos map[uint64]*TxInodeInfo
|
|
TxDentryInfos map[string]*TxDentryInfo
|
|
LastCheckTime int64
|
|
}
|
|
|
|
type TxMpInfo struct {
|
|
MpId uint64
|
|
Members string
|
|
TxInodeInfos map[uint64]*TxInodeInfo
|
|
TxDentryInfos map[string]*TxDentryInfo
|
|
}
|
|
|
|
const InitInode = 0
|
|
|
|
func (tx *TransactionInfo) SetCreateInodeId(ino uint64) {
|
|
inoIfo := tx.TxInodeInfos[InitInode]
|
|
inoIfo.Ino = ino
|
|
delete(tx.TxInodeInfos, InitInode)
|
|
tx.TxInodeInfos[ino] = inoIfo
|
|
}
|
|
|
|
func (tx *TransactionInfo) GroupByMp() map[uint64]*TxMpInfo {
|
|
txMap := make(map[uint64]*TxMpInfo)
|
|
|
|
for k, ifo := range tx.TxInodeInfos {
|
|
mpIfo, ok := txMap[ifo.MpID]
|
|
if !ok {
|
|
mpIfo = &TxMpInfo{
|
|
MpId: ifo.MpID,
|
|
Members: ifo.MpMembers,
|
|
TxInodeInfos: make(map[uint64]*TxInodeInfo),
|
|
TxDentryInfos: make(map[string]*TxDentryInfo),
|
|
}
|
|
txMap[ifo.MpID] = mpIfo
|
|
}
|
|
|
|
mpIfo.TxInodeInfos[k] = ifo
|
|
}
|
|
|
|
for k, ifo := range tx.TxDentryInfos {
|
|
mpIfo, ok := txMap[ifo.MpID]
|
|
if !ok {
|
|
mpIfo = &TxMpInfo{
|
|
MpId: ifo.MpID,
|
|
Members: ifo.MpMembers,
|
|
TxInodeInfos: make(map[uint64]*TxInodeInfo),
|
|
TxDentryInfos: make(map[string]*TxDentryInfo),
|
|
}
|
|
txMap[ifo.MpID] = mpIfo
|
|
}
|
|
|
|
mpIfo.TxDentryInfos[k] = ifo
|
|
}
|
|
|
|
return txMap
|
|
}
|
|
|
|
func (tx *TransactionInfo) IsDone() bool {
|
|
return tx.State == TxStateCommitDone || tx.State == TxStateRollbackDone
|
|
}
|
|
|
|
func (tx *TransactionInfo) CanDelete() bool {
|
|
if !tx.Finish() {
|
|
return false
|
|
}
|
|
|
|
if tx.DoneTime+DefaultTxDeleteTime < time.Now().Unix() {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (tx *TransactionInfo) NeedClearOrphan() bool {
|
|
if tx.Finish() {
|
|
return false
|
|
}
|
|
|
|
now := time.Now().Unix()
|
|
if tx.CreateTime+ClearOrphanTxTime > now {
|
|
return false
|
|
}
|
|
|
|
// try to check every 1 minutes to avoid too many request
|
|
if now-tx.LastCheckTime < 60 {
|
|
return false
|
|
}
|
|
|
|
tx.LastCheckTime = now
|
|
return true
|
|
}
|
|
|
|
func (tx *TransactionInfo) Finish() bool {
|
|
return tx.RMFinish
|
|
}
|
|
|
|
func (tx *TransactionInfo) SetFinish() {
|
|
tx.RMFinish = true
|
|
tx.DoneTime = time.Now().Unix()
|
|
}
|
|
|
|
func (txInfo *TransactionInfo) GetInfo() string {
|
|
return txInfo.String()
|
|
}
|
|
|
|
func (txInfo *TransactionInfo) IsExpired() (expired bool) {
|
|
now := time.Now().Unix()
|
|
expired = txInfo.Timeout*60+txInfo.CreateTime < now
|
|
if expired {
|
|
log.LogWarnf("IsExpired: transaction [%v] is expired, now[%v], CreateTime[%v]", txInfo, now, txInfo.CreateTime)
|
|
}
|
|
return expired
|
|
}
|
|
|
|
// Less tests whether the current TransactionInfo item is less than the given one.
|
|
// This method is necessary fot B-Tree item implementation.
|
|
func (txInfo *TransactionInfo) Less(than btree.Item) bool {
|
|
ti, ok := than.(*TransactionInfo)
|
|
return ok && txInfo.TxID < ti.TxID
|
|
}
|
|
|
|
// Copy returns a copy of the inode.
|
|
func (txInfo *TransactionInfo) Copy() btree.Item {
|
|
return txInfo.GetCopy()
|
|
}
|
|
|
|
func NewTxInfoBItem(txId string) *TransactionInfo {
|
|
return &TransactionInfo{
|
|
TxID: txId,
|
|
}
|
|
}
|
|
|
|
const initTmId = -1
|
|
|
|
func NewTransactionInfo(timeout int64, txType uint32) *TransactionInfo {
|
|
return &TransactionInfo{
|
|
Timeout: timeout,
|
|
TxInodeInfos: make(map[uint64]*TxInodeInfo),
|
|
TxDentryInfos: make(map[string]*TxDentryInfo),
|
|
TmID: initTmId,
|
|
TxType: txType,
|
|
State: TxStateInit,
|
|
}
|
|
}
|
|
|
|
func (txInfo *TransactionInfo) IsInitialized() bool {
|
|
return txInfo.TxID != ""
|
|
}
|
|
|
|
func (txInfo *TransactionInfo) String() string {
|
|
data, err := json.Marshal(txInfo)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return string(data)
|
|
}
|
|
|
|
func (txInfo *TransactionInfo) GetCopy() *TransactionInfo {
|
|
newInfo := *txInfo
|
|
return &newInfo
|
|
}
|
|
|
|
func (txInfo *TransactionInfo) Marshal() (result []byte, err error) {
|
|
buff := bytes.NewBuffer(make([]byte, 0, 256))
|
|
id := []byte(txInfo.TxID)
|
|
idSize := uint32(len(id))
|
|
if err = binary.Write(buff, binary.BigEndian, &idSize); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err = buff.Write(id); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = binary.Write(buff, binary.BigEndian, &txInfo.TxType); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = binary.Write(buff, binary.BigEndian, &txInfo.TmID); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = binary.Write(buff, binary.BigEndian, &txInfo.CreateTime); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = binary.Write(buff, binary.BigEndian, &txInfo.Timeout); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = binary.Write(buff, binary.BigEndian, &txInfo.State); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = binary.Write(buff, binary.BigEndian, &txInfo.DoneTime); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = binary.Write(buff, binary.BigEndian, &txInfo.RMFinish); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
inodeNum := uint32(len(txInfo.TxInodeInfos))
|
|
if err = binary.Write(buff, binary.BigEndian, &inodeNum); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, txInodeInfo := range txInfo.TxInodeInfos {
|
|
bs, err := txInodeInfo.Marshal()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = binary.Write(buff, binary.BigEndian, uint32(len(bs))); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := buff.Write(bs); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
dentryNum := uint32(len(txInfo.TxDentryInfos))
|
|
if err = binary.Write(buff, binary.BigEndian, &dentryNum); err != nil {
|
|
panic(err)
|
|
}
|
|
for _, txDentryInfo := range txInfo.TxDentryInfos {
|
|
bs, err := txDentryInfo.Marshal()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = binary.Write(buff, binary.BigEndian, uint32(len(bs))); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := buff.Write(bs); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return buff.Bytes(), nil
|
|
}
|
|
|
|
func (txInfo *TransactionInfo) Unmarshal(raw []byte) (err error) {
|
|
buff := bytes.NewBuffer(raw)
|
|
idSize := uint32(0)
|
|
if err = binary.Read(buff, binary.BigEndian, &idSize); err != nil {
|
|
return
|
|
}
|
|
if idSize > 0 {
|
|
if idSize > MaxBufferSize {
|
|
return ErrBufferSizeExceedMaximum
|
|
}
|
|
id := make([]byte, idSize)
|
|
if _, err = io.ReadFull(buff, id); err != nil {
|
|
return
|
|
}
|
|
txInfo.TxID = string(id)
|
|
}
|
|
|
|
if err = binary.Read(buff, binary.BigEndian, &txInfo.TxType); err != nil {
|
|
return
|
|
}
|
|
|
|
if err = binary.Read(buff, binary.BigEndian, &txInfo.TmID); err != nil {
|
|
return
|
|
}
|
|
if err = binary.Read(buff, binary.BigEndian, &txInfo.CreateTime); err != nil {
|
|
return
|
|
}
|
|
if err = binary.Read(buff, binary.BigEndian, &txInfo.Timeout); err != nil {
|
|
return
|
|
}
|
|
if err = binary.Read(buff, binary.BigEndian, &txInfo.State); err != nil {
|
|
return
|
|
}
|
|
if err = binary.Read(buff, binary.BigEndian, &txInfo.DoneTime); err != nil {
|
|
return
|
|
}
|
|
if err = binary.Read(buff, binary.BigEndian, &txInfo.RMFinish); err != nil {
|
|
return
|
|
}
|
|
|
|
var inodeNum uint32
|
|
if err = binary.Read(buff, binary.BigEndian, &inodeNum); err != nil {
|
|
return
|
|
}
|
|
var dataLen uint32
|
|
txInfo.TxInodeInfos = map[uint64]*TxInodeInfo{}
|
|
for i := uint32(0); i < inodeNum; i++ {
|
|
if err = binary.Read(buff, binary.BigEndian, &dataLen); err != nil {
|
|
return
|
|
}
|
|
if dataLen > MaxBufferSize {
|
|
return ErrBufferSizeExceedMaximum
|
|
}
|
|
data := make([]byte, int(dataLen))
|
|
if _, err = buff.Read(data); err != nil {
|
|
return
|
|
}
|
|
txInodeInfo := NewTxInodeInfo("", 0, 0)
|
|
if err = txInodeInfo.Unmarshal(data); err != nil {
|
|
return
|
|
}
|
|
txInfo.TxInodeInfos[txInodeInfo.GetKey()] = txInodeInfo
|
|
}
|
|
|
|
var dentryNum uint32
|
|
txInfo.TxDentryInfos = map[string]*TxDentryInfo{}
|
|
if err = binary.Read(buff, binary.BigEndian, &dentryNum); err != nil {
|
|
return
|
|
}
|
|
|
|
for i := uint32(0); i < dentryNum; i++ {
|
|
if err = binary.Read(buff, binary.BigEndian, &dataLen); err != nil {
|
|
return
|
|
}
|
|
if dataLen > MaxBufferSize {
|
|
return ErrBufferSizeExceedMaximum
|
|
}
|
|
data := make([]byte, int(dataLen))
|
|
if _, err = buff.Read(data); err != nil {
|
|
return
|
|
}
|
|
txDentryInfo := NewTxDentryInfo("", 0, "", 0)
|
|
if err = txDentryInfo.Unmarshal(data); err != nil {
|
|
return
|
|
}
|
|
txInfo.TxDentryInfos[txDentryInfo.GetKey()] = txDentryInfo
|
|
}
|
|
|
|
return
|
|
}
|