github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/istructsmem/internal/containers/impl.go (about) 1 /* 2 * Copyright (c) 2021-present Sigma-Soft, Ltd. 3 * @author: Nikolay Nikitin 4 */ 5 6 package containers 7 8 import ( 9 "context" 10 "encoding/binary" 11 "errors" 12 "fmt" 13 14 "github.com/voedger/voedger/pkg/appdef" 15 "github.com/voedger/voedger/pkg/istorage" 16 "github.com/voedger/voedger/pkg/istructsmem/internal/consts" 17 "github.com/voedger/voedger/pkg/istructsmem/internal/utils" 18 "github.com/voedger/voedger/pkg/istructsmem/internal/vers" 19 ) 20 21 func newContainers() *Containers { 22 return &Containers{ 23 containers: make(map[string]ContainerID), 24 ids: make(map[ContainerID]string), 25 lastID: ContainerNameIDSysLast, 26 } 27 } 28 29 // Retrieve container for specified ID 30 func (cnt *Containers) Container(id ContainerID) (name string, err error) { 31 name, ok := cnt.ids[id] 32 if ok { 33 return name, nil 34 } 35 36 return "", fmt.Errorf("unknown container ID «%v»: %w", id, ErrContainerIDNotFound) 37 } 38 39 // Retrieve ID for specified container 40 func (cnt *Containers) ID(name string) (ContainerID, error) { 41 if id, ok := cnt.containers[name]; ok { 42 return id, nil 43 } 44 return 0, fmt.Errorf("unknown container name «%v»: %w", name, ErrContainerNotFound) 45 } 46 47 // Loads all container from storage, add all known system and application containers and store if some changes. Must be called at application starts 48 func (cnt *Containers) Prepare(storage istorage.IAppStorage, versions *vers.Versions, appDef appdef.IAppDef) (err error) { 49 if err = cnt.load(storage, versions); err != nil { 50 return err 51 } 52 53 if err = cnt.collectAll(appDef); err != nil { 54 return err 55 } 56 57 if cnt.changes > 0 { 58 if err := cnt.store(storage, versions); err != nil { 59 return err 60 } 61 } 62 63 return nil 64 } 65 66 // Retrieves and stores IDs for all known containers in application types. Must be called then application starts 67 func (cnt *Containers) collectAll(appDef appdef.IAppDef) (err error) { 68 69 // system containers 70 cnt.collectSys("", NullContainerID) 71 72 // application containers 73 if appDef != nil { 74 appDef.Types( 75 func(t appdef.IType) { 76 if cont, ok := t.(appdef.IContainers); ok { 77 for _, c := range cont.Containers() { 78 err = errors.Join(err, cnt.collect(c.Name())) 79 } 80 } 81 }) 82 } 83 84 return err 85 } 86 87 // Retrieves and stores ID for specified application container 88 func (cnt *Containers) collect(name string) (err error) { 89 if _, ok := cnt.containers[name]; ok { 90 return nil // already known container 91 } 92 93 for id := cnt.lastID + 1; id < MaxAvailableContainerID; id++ { 94 if _, ok := cnt.ids[id]; !ok { 95 cnt.containers[name] = id 96 cnt.ids[id] = name 97 cnt.lastID = id 98 cnt.changes++ 99 return nil 100 } 101 } 102 103 return ErrContainerIDsExceeds 104 } 105 106 // Remember ID for specified system container 107 func (cnt *Containers) collectSys(name string, id ContainerID) { 108 cnt.containers[name] = id 109 cnt.ids[id] = name 110 } 111 112 // Loads all stored container from storage 113 func (cnt *Containers) load(storage istorage.IAppStorage, versions *vers.Versions) (err error) { 114 115 ver := versions.Get(vers.SysContainersVersion) 116 switch ver { 117 case vers.UnknownVersion: // no sys.Container storage exists 118 return nil 119 case ver01: 120 return cnt.load01(storage) 121 } 122 123 return fmt.Errorf("unknown version of system Containers view (%v): %w", ver, vers.ErrorInvalidVersion) 124 } 125 126 // Loads all stored containers from storage version ver01 127 func (cnt *Containers) load01(storage istorage.IAppStorage) error { 128 129 readName := func(cCols, value []byte) error { 130 name := string(cCols) 131 if ok, err := appdef.ValidIdent(name); !ok { 132 return err 133 } 134 id := ContainerID(binary.BigEndian.Uint16(value)) 135 if id == NullContainerID { 136 return nil // deleted Container 137 } 138 139 if id <= ContainerNameIDSysLast { 140 return fmt.Errorf("unexpected ID (%v) is loaded from system Containers view: %w", id, ErrWrongContainerID) 141 } 142 143 cnt.containers[name] = id 144 cnt.ids[id] = name 145 146 if cnt.lastID < id { 147 cnt.lastID = id 148 } 149 150 return nil 151 } 152 153 pKey := utils.ToBytes(consts.SysView_Containers, ver01) 154 return storage.Read(context.Background(), pKey, nil, nil, readName) 155 } 156 157 // Stores all known container to storage 158 func (cnt *Containers) store(storage istorage.IAppStorage, versions *vers.Versions) (err error) { 159 pKey := utils.ToBytes(consts.SysView_Containers, latestVersion) 160 161 batch := make([]istorage.BatchItem, 0) 162 for name, id := range cnt.containers { 163 if name == "" { 164 continue // skip NullContainerID 165 } 166 item := istorage.BatchItem{ 167 PKey: pKey, 168 CCols: []byte(name), 169 Value: utils.ToBytes(id), 170 } 171 batch = append(batch, item) 172 } 173 174 if err = storage.PutBatch(batch); err != nil { 175 return fmt.Errorf("error store application container IDs to storage: %w", err) 176 } 177 178 if ver := versions.Get(vers.SysContainersVersion); ver != latestVersion { 179 if err = versions.Put(vers.SysContainersVersion, latestVersion); err != nil { 180 return fmt.Errorf("error store system Containers view version: %w", err) 181 } 182 } 183 184 cnt.changes = 0 185 return nil 186 }