github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/fs/content.go (about) 1 // Package fs provides mountpath and FQN abstractions and methods to resolve/map stored content 2 /* 3 * Copyright (c) 2018-2021, NVIDIA CORPORATION. All rights reserved. 4 */ 5 package fs 6 7 import ( 8 "fmt" 9 "path/filepath" 10 "strconv" 11 "strings" 12 13 "github.com/NVIDIA/aistore/cmn" 14 "github.com/NVIDIA/aistore/cmn/cos" 15 "github.com/NVIDIA/aistore/cmn/debug" 16 "github.com/NVIDIA/aistore/cmn/nlog" 17 ) 18 19 /* 20 * Besides objects we must deal with additional files like: workfiles, dsort 21 * intermediate files (used when spilling to disk) or EC slices. These files can 22 * have different rules of rebalancing, evicting and other processing. Each 23 * content type needs to implement ContentResolver to reflect the rules and 24 * permission for different services. To see how the interface can be 25 * implemented see: DefaultWorkfile implemention. 26 * 27 * When walking through the files we need to know if the file is an object or 28 * other content. To do that we generate fqn with Gen. It adds short 29 * prefix to the base name, which we believe is unique and will separate objects 30 * from content files. We parse the file type to run ParseUniqueFQN (implemented 31 * by this file type) on the rest of the base name. 32 */ 33 34 const ( 35 contentTypeLen = 2 36 37 ObjectType = "ob" 38 WorkfileType = "wk" 39 ECSliceType = "ec" 40 ECMetaType = "mt" 41 ) 42 43 type ( 44 ContentResolver interface { 45 // When set to true, services like rebalance have permission to move 46 // content for example to another target because it is misplaced (HRW). 47 PermToMove() bool 48 // When set to true, services like LRU have permission to evict/delete content 49 PermToEvict() bool 50 // When set to true, content can be checksumed, shown or processed in other ways. 51 PermToProcess() bool 52 53 // Generates unique base name for original one. This function may add 54 // additional information to the base name. 55 // prefix - user-defined marker 56 GenUniqueFQN(base, prefix string) (ufqn string) 57 // Parses generated unique fqn to the original one. 58 ParseUniqueFQN(base string) (orig string, old, ok bool) 59 } 60 61 PartsFQN interface { 62 ObjectName() string 63 Bucket() *cmn.Bck 64 Mountpath() *Mountpath 65 CacheIdx() int 66 } 67 68 ContentInfo struct { 69 Dir string // original directory 70 Base string // original basename 71 Type string // content type 72 Old bool // true if old (subj. to space cleanup) 73 } 74 75 contentSpecMgr struct { 76 m map[string]ContentResolver 77 } 78 ) 79 80 var CSM *contentSpecMgr 81 82 func (f *contentSpecMgr) Resolver(contentType string) ContentResolver { 83 r := f.m[contentType] 84 return r 85 } 86 87 // Reg registers new content type with a given content resolver. 88 // NOTE: all content type registrations must happen at startup. 89 func (f *contentSpecMgr) Reg(contentType string, spec ContentResolver, unitTest ...bool) { 90 err := f._reg(contentType, spec) 91 if err != nil && len(unitTest) == 0 { 92 debug.Assert(false) 93 cos.ExitLog(err) 94 } 95 } 96 97 func (f *contentSpecMgr) _reg(contentType string, spec ContentResolver) error { 98 if strings.ContainsRune(contentType, filepath.Separator) { 99 return fmt.Errorf("%s content type cannot contain %q", contentType, filepath.Separator) 100 } 101 if len(contentType) != contentTypeLen { 102 return fmt.Errorf("%s content type must have length %d", contentType, contentTypeLen) 103 } 104 if _, ok := f.m[contentType]; ok { 105 return fmt.Errorf("%s content type is already registered", contentType) 106 } 107 f.m[contentType] = spec 108 return nil 109 } 110 111 // Gen returns a new FQN generated from given parts. 112 func (f *contentSpecMgr) Gen(parts PartsFQN, contentType, prefix string) (fqn string) { 113 var ( 114 spec = f.m[contentType] 115 objName = spec.GenUniqueFQN(parts.ObjectName(), prefix) 116 ) 117 return parts.Mountpath().MakePathFQN(parts.Bucket(), contentType, objName) 118 } 119 120 // FileSpec returns the specification/attributes and information about the `fqn` 121 // (which must be generated by the Gen) 122 func (f *contentSpecMgr) FileSpec(fqn string) (resolver ContentResolver, info *ContentInfo) { 123 dir, base := filepath.Split(fqn) 124 if dir == "" || base == "" { 125 return 126 } 127 debug.Assert(cos.IsLastB(dir, filepath.Separator), dir) 128 129 var parsed ParsedFQN 130 if err := parsed.Init(fqn); err != nil { 131 return 132 } 133 spec, found := f.m[parsed.ContentType] 134 if !found { 135 nlog.Errorf("%q: unknown content type %s", fqn, parsed.ContentType) 136 return 137 } 138 origBase, old, ok := spec.ParseUniqueFQN(base) 139 if !ok { 140 return 141 } 142 resolver = spec 143 info = &ContentInfo{Dir: dir, Base: origBase, Old: old, Type: parsed.ContentType} 144 return 145 } 146 147 func (f *contentSpecMgr) PermToEvict(fqn string) (ok, isOld bool) { 148 spec, info := f.FileSpec(fqn) 149 if spec == nil { 150 return true, false 151 } 152 153 return spec.PermToEvict(), info.Old 154 } 155 156 func (f *contentSpecMgr) PermToMove(fqn string) (ok bool) { 157 spec, _ := f.FileSpec(fqn) 158 if spec == nil { 159 return false 160 } 161 162 return spec.PermToMove() 163 } 164 165 func (f *contentSpecMgr) PermToProcess(fqn string) (ok bool) { 166 spec, _ := f.FileSpec(fqn) 167 if spec == nil { 168 return false 169 } 170 171 return spec.PermToProcess() 172 } 173 174 // FIXME: This should be probably placed somewhere else \/ 175 176 type ( 177 ObjectContentResolver struct{} 178 WorkfileContentResolver struct{} 179 ECSliceContentResolver struct{} 180 ECMetaContentResolver struct{} 181 ) 182 183 func (*ObjectContentResolver) PermToMove() bool { return true } 184 func (*ObjectContentResolver) PermToEvict() bool { return true } 185 func (*ObjectContentResolver) PermToProcess() bool { return true } 186 func (*ObjectContentResolver) GenUniqueFQN(base, _ string) string { return base } 187 188 func (*ObjectContentResolver) ParseUniqueFQN(base string) (orig string, old, ok bool) { 189 return base, false, true 190 } 191 192 func (*WorkfileContentResolver) PermToMove() bool { return false } 193 func (*WorkfileContentResolver) PermToEvict() bool { return true } 194 func (*WorkfileContentResolver) PermToProcess() bool { return false } 195 196 func (*WorkfileContentResolver) GenUniqueFQN(base, prefix string) string { 197 const ( 198 contentSepa = "." 199 ) 200 var ( 201 dir, fname = filepath.Split(base) 202 tieBreaker = cos.GenTie() 203 ) 204 fname = prefix + contentSepa + fname 205 base = filepath.Join(dir, fname) 206 return base + contentSepa + tieBreaker + contentSepa + spid 207 } 208 209 func (*WorkfileContentResolver) ParseUniqueFQN(base string) (orig string, old, ok bool) { 210 const ( 211 contentSepa = '.' 212 ) 213 // remove original content type 214 cntIndex := strings.IndexByte(base, contentSepa) 215 if cntIndex < 0 { 216 return "", false, false 217 } 218 base = base[cntIndex+1:] 219 220 pidIndex := strings.LastIndexByte(base, contentSepa) // pid 221 if pidIndex < 0 { 222 return "", false, false 223 } 224 tieIndex := strings.LastIndexByte(base[:pidIndex], contentSepa) // tie breaker 225 if tieIndex < 0 { 226 return "", false, false 227 } 228 filePID, err := strconv.ParseInt(base[pidIndex+1:], 16, 64) 229 if err != nil { 230 return "", false, false 231 } 232 233 return base[:tieIndex], filePID != pid, true 234 } 235 236 func (*ECSliceContentResolver) PermToMove() bool { return true } 237 func (*ECSliceContentResolver) PermToEvict() bool { return true } 238 func (*ECSliceContentResolver) PermToProcess() bool { return false } 239 240 func (*ECSliceContentResolver) GenUniqueFQN(base, _ string) string { return base } 241 242 func (*ECSliceContentResolver) ParseUniqueFQN(base string) (orig string, old, ok bool) { 243 return base, false, true 244 } 245 246 func (*ECMetaContentResolver) PermToMove() bool { return true } 247 func (*ECMetaContentResolver) PermToEvict() bool { return true } 248 func (*ECMetaContentResolver) PermToProcess() bool { return false } 249 250 func (*ECMetaContentResolver) GenUniqueFQN(base, _ string) string { return base } 251 252 func (*ECMetaContentResolver) ParseUniqueFQN(base string) (orig string, old, ok bool) { 253 return base, false, true 254 }