github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/ec/metafile.go (about) 1 // Package ec provides erasure coding (EC) based data protection for AIStore. 2 /* 3 * Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved. 4 */ 5 package ec 6 7 import ( 8 "fmt" 9 "io" 10 "os" 11 12 "github.com/NVIDIA/aistore/cmn/cos" 13 "github.com/NVIDIA/aistore/core" 14 "github.com/NVIDIA/aistore/core/meta" 15 "github.com/NVIDIA/aistore/fs" 16 "github.com/OneOfOne/xxhash" 17 ) 18 19 const MDVersionLast = 1 // current version of metadata 20 21 // Metadata - EC information stored in metafiles for every encoded object 22 type Metadata struct { 23 Size int64 `json:"obj_size"` // obj size (after EC'ing sum size of slices differs from the original) 24 Generation int64 `json:"generation"` // Timestamp when the object was EC'ed 25 ObjCksum string `json:"obj_cksum"` // checksum of the original object 26 ObjVersion string `json:"obj_version"` // object version 27 CksumType string `json:"cksum_type"` // slice checksum type 28 CksumValue string `json:"slice_cksum"` // slice checksum of the slice if EC is used 29 FullReplica string `json:"replica_node"` // daemon ID where full(main) replica is 30 Daemons cos.MapStrUint16 `json:"nodes"` // Locations of all slices: DaemonID <-> SliceID 31 Data int `json:"data_slices"` // the number of data slices 32 Parity int `json:"parity_slices"` // the number of parity slices 33 SliceID int `json:"slice_id"` // 0 for full replica, 1 to N for slices 34 MDVersion uint32 `json:"md_version"` // Metadata format version 35 IsCopy bool `json:"is_copy"` // object is replicated(true) or encoded(false) 36 } 37 38 // interface guard 39 var ( 40 _ cos.Unpacker = (*Metadata)(nil) 41 _ cos.Packer = (*Metadata)(nil) 42 ) 43 44 func NewMetadata() *Metadata { 45 return &Metadata{MDVersion: MDVersionLast} 46 } 47 48 // LoadMetadata loads and parses EC metadata from a file 49 func LoadMetadata(fqn string) (*Metadata, error) { 50 b, err := os.ReadFile(fqn) 51 if err != nil { 52 return nil, err 53 } 54 md := &Metadata{} 55 unpacker := cos.NewUnpacker(b) 56 if err := unpacker.ReadAny(md); err != nil { 57 err := fmt.Errorf("damaged metafile %q: %v", fqn, err) 58 return nil, err 59 } 60 61 return md, nil 62 } 63 64 func MetaFromReader(reader io.Reader) (*Metadata, error) { 65 b, err := io.ReadAll(reader) 66 if err != nil { 67 return nil, err 68 } 69 md := &Metadata{} 70 unpacker := cos.NewUnpacker(b) 71 err = unpacker.ReadAny(md) 72 return md, err 73 } 74 75 // RemoteTargets returns list of Snodes that contain a slice or replica. 76 // This target(`t`) is removed from the list. 77 func (md *Metadata) RemoteTargets() []*meta.Snode { 78 if len(md.Daemons) == 0 { 79 return nil 80 } 81 nodes := make([]*meta.Snode, 0, len(md.Daemons)) 82 smap := core.T.Sowner().Get() 83 for tid := range md.Daemons { 84 if tid == core.T.SID() { 85 continue 86 } 87 tsi := smap.GetTarget(tid) 88 if tsi != nil { 89 nodes = append(nodes, tsi) 90 } 91 } 92 return nodes 93 } 94 95 // TODO: use 'buf, slab = smm.Alloc()' 96 func (md *Metadata) NewPack() []byte { 97 var ( 98 buf []byte 99 l = md.PackedSize() 100 ) 101 packer := cos.NewPacker(buf, l) 102 packer.WriteAny(md) 103 return packer.Bytes() 104 } 105 106 func (md *Metadata) Clone() *Metadata { 107 clone := &Metadata{} 108 cos.CopyStruct(clone, md) 109 return clone 110 } 111 112 // ObjectMetadata returns metadata for an object or its slice if any exists 113 func ObjectMetadata(bck *meta.Bck, objName string) (*Metadata, error) { 114 fqn, _, err := core.HrwFQN(bck.Bucket(), fs.ECMetaType, objName) 115 if err != nil { 116 return nil, err 117 } 118 return LoadMetadata(fqn) 119 } 120 121 func (md *Metadata) Unpack(unpacker *cos.ByteUnpack) (err error) { 122 var cksum uint64 123 if md.MDVersion, err = unpacker.ReadUint32(); err != nil { 124 return 125 } 126 switch md.MDVersion { 127 case MDVersionLast: 128 err = md.unpackLastVersion(unpacker) 129 default: 130 err = fmt.Errorf("unsupported metadata format version %d. Only %d supported", 131 md.MDVersion, MDVersionLast) 132 } 133 if err != nil { 134 return 135 } 136 137 if cksum, err = unpacker.ReadUint64(); err != nil { 138 return 139 } 140 b := unpacker.Bytes() 141 calcCksum := xxhash.Checksum64S(b[:len(b)-cos.SizeofI64], cos.MLCG32) 142 if cksum != calcCksum { 143 err = cos.NewErrMetaCksum(cksum, calcCksum, "EC metadata") 144 } 145 return err 146 } 147 148 func (md *Metadata) unpackLastVersion(unpacker *cos.ByteUnpack) (err error) { 149 var i16 uint16 150 if md.Generation, err = unpacker.ReadInt64(); err != nil { 151 return 152 } 153 if md.Size, err = unpacker.ReadInt64(); err != nil { 154 return 155 } 156 if i16, err = unpacker.ReadUint16(); err != nil { 157 return 158 } 159 md.Data = int(i16) 160 if i16, err = unpacker.ReadUint16(); err != nil { 161 return 162 } 163 md.Parity = int(i16) 164 if i16, err = unpacker.ReadUint16(); err != nil { 165 return 166 } 167 md.SliceID = int(i16) 168 if md.IsCopy, err = unpacker.ReadBool(); err != nil { 169 return 170 } 171 if md.FullReplica, err = unpacker.ReadString(); err != nil { 172 return 173 } 174 if md.ObjCksum, err = unpacker.ReadString(); err != nil { 175 return 176 } 177 if md.ObjVersion, err = unpacker.ReadString(); err != nil { 178 return 179 } 180 if md.CksumType, err = unpacker.ReadString(); err != nil { 181 return 182 } 183 if md.CksumValue, err = unpacker.ReadString(); err != nil { 184 return 185 } 186 md.Daemons, err = unpacker.ReadMapStrUint16() 187 return 188 } 189 190 func (md *Metadata) Pack(packer *cos.BytePack) { 191 packer.WriteUint32(md.MDVersion) 192 packer.WriteInt64(md.Generation) 193 packer.WriteInt64(md.Size) 194 packer.WriteUint16(uint16(md.Data)) 195 packer.WriteUint16(uint16(md.Parity)) 196 packer.WriteUint16(uint16(md.SliceID)) 197 packer.WriteBool(md.IsCopy) 198 packer.WriteString(md.FullReplica) 199 packer.WriteString(md.ObjCksum) 200 packer.WriteString(md.ObjVersion) 201 packer.WriteString(md.CksumType) 202 packer.WriteString(md.CksumValue) 203 packer.WriteMapStrUint16(md.Daemons) 204 h := xxhash.Checksum64S(packer.Bytes(), cos.MLCG32) 205 packer.WriteUint64(h) 206 } 207 208 func (md *Metadata) PackedSize() int { 209 daemonListSz := cos.SizeofLen 210 for k := range md.Daemons { 211 daemonListSz += cos.PackedStrLen(k) + cos.SizeofI16 212 } 213 return cos.SizeofI32 + cos.SizeofI64*2 + cos.SizeofI16*3 + 1 /*isCopy*/ + 214 cos.PackedStrLen(md.ObjCksum) + cos.PackedStrLen(md.ObjVersion) + 215 cos.PackedStrLen(md.CksumType) + cos.PackedStrLen(md.CksumValue) + 216 cos.PackedStrLen(md.FullReplica) + daemonListSz + cos.SizeofI64 /*md cksum*/ 217 }