github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/istructsmem/internal/singletons/impl.go (about) 1 /* 2 * Copyright (c) 2021-present Sigma-Soft, Ltd. 3 * @author: Nikolay Nikitin 4 */ 5 6 package singletons 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/istructs" 17 "github.com/voedger/voedger/pkg/istructsmem/internal/consts" 18 "github.com/voedger/voedger/pkg/istructsmem/internal/utils" 19 "github.com/voedger/voedger/pkg/istructsmem/internal/vers" 20 ) 21 22 func newSingletons() *Singletons { 23 return &Singletons{ 24 qNames: make(map[appdef.QName]istructs.RecordID), 25 ids: make(map[istructs.RecordID]appdef.QName), 26 lastID: istructs.FirstSingletonID - 1, 27 } 28 } 29 30 // Returns ID for singleton with specified QName 31 func (st *Singletons) ID(qName appdef.QName) (istructs.RecordID, error) { 32 if id, ok := st.qNames[qName]; ok { 33 return id, nil 34 } 35 return istructs.NullRecordID, fmt.Errorf("unable to find singleton ID for type «%v»: %w", qName, ErrNameNotFound) 36 } 37 38 // Loads all singletons IDs from storage, add all known application singletons and store if some changes. 39 // Must be called at application starts 40 func (st *Singletons) Prepare(storage istorage.IAppStorage, versions *vers.Versions, appDef appdef.IAppDef) (err error) { 41 if err = st.load(storage, versions); err != nil { 42 return err 43 } 44 45 if appDef != nil { 46 if err = st.collectAllSingletons(appDef); err != nil { 47 return err 48 } 49 } 50 51 if st.changes > 0 { 52 if err := st.store(storage, versions); err != nil { 53 return err 54 } 55 } 56 57 return nil 58 } 59 60 // loads all stored singleton IDs from storage 61 func (st *Singletons) load(storage istorage.IAppStorage, versions *vers.Versions) (err error) { 62 ver := versions.Get(vers.SysSingletonsVersion) 63 switch ver { 64 case vers.UnknownVersion: // no system singletons view exists in storage 65 return nil 66 case ver01: 67 return st.load01(storage) 68 } 69 70 return fmt.Errorf("unable load singleton IDs from system view version %v: %w", ver, vers.ErrorInvalidVersion) 71 } 72 73 // Loads singletons IDs from storage using ver01 codec 74 func (st *Singletons) load01(storage istorage.IAppStorage) error { 75 76 readSingleton := func(cCols, value []byte) error { 77 qName, err := appdef.ParseQName(string(cCols)) 78 if err != nil { 79 return err 80 } 81 id := istructs.RecordID(binary.BigEndian.Uint64(value)) 82 83 st.qNames[qName] = id 84 st.ids[id] = qName 85 86 if st.lastID < id { 87 st.lastID = id 88 } 89 90 return nil 91 } 92 93 pKey := utils.ToBytes(consts.SysView_SingletonIDs, ver01) 94 return storage.Read(context.Background(), pKey, nil, nil, readSingleton) 95 } 96 97 // Collect all application singleton IDs 98 func (st *Singletons) collectAllSingletons(appDef appdef.IAppDef) (err error) { 99 appDef.Types( 100 func(t appdef.IType) { 101 if singleton, ok := t.(appdef.ISingleton); ok { 102 if singleton.Singleton() { 103 err = errors.Join(err, 104 st.collectSingleton(singleton.QName())) 105 } 106 } 107 }) 108 109 return err 110 } 111 112 // collectSingleton checks is singleton in cache. If not then adds it with new ID 113 func (st *Singletons) collectSingleton(qname appdef.QName) error { 114 115 if _, ok := st.qNames[qname]; ok { 116 return nil // already known singleton 117 } 118 119 for id := st.lastID + 1; id < istructs.MaxSingletonID; id++ { 120 if _, ok := st.ids[id]; !ok { 121 st.qNames[qname] = id 122 st.ids[id] = qname 123 st.lastID = id 124 st.changes++ 125 return nil 126 } 127 } 128 129 return ErrSingletonIDsExceeds 130 } 131 132 // stores singletons IDs using latestVersion codec 133 func (st *Singletons) store(storage istorage.IAppStorage, versions *vers.Versions) (err error) { 134 pKey := utils.ToBytes(consts.SysView_SingletonIDs, latestVersion) 135 136 batch := make([]istorage.BatchItem, 0) 137 for qName, id := range st.qNames { 138 if id >= istructs.FirstSingletonID { 139 item := istorage.BatchItem{ 140 PKey: pKey, 141 CCols: []byte(qName.String()), 142 Value: utils.ToBytes(uint64(id)), 143 } 144 batch = append(batch, item) 145 } 146 } 147 148 if err = storage.PutBatch(batch); err != nil { 149 return fmt.Errorf("error store application singleton IDs to storage: %w", err) 150 } 151 152 if ver := versions.Get(vers.SysSingletonsVersion); ver != latestVersion { 153 if err = versions.Put(vers.SysSingletonsVersion, latestVersion); err != nil { 154 return fmt.Errorf("error store singletons system view version: %w", err) 155 } 156 } 157 158 st.changes = 0 159 return nil 160 }