github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/system/storage/storage.go (about) 1 // This file is part of the Smart Home 2 // Program complex distribution https://github.com/e154/smart-home 3 // Copyright (C) 2016-2023, Filippov Alex 4 // 5 // This library is free software: you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 3 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Library General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library. If not, see 17 // <https://www.gnu.org/licenses/>. 18 19 package storage 20 21 import ( 22 "context" 23 "fmt" 24 "strings" 25 "sync" 26 "time" 27 28 "go.uber.org/atomic" 29 30 "github.com/e154/smart-home/adaptors" 31 "github.com/e154/smart-home/common/events" 32 "github.com/e154/smart-home/common/logger" 33 m "github.com/e154/smart-home/models" 34 "github.com/e154/smart-home/system/bus" 35 ) 36 37 var ( 38 log = logger.MustGetLogger("storage") 39 ) 40 41 // Storage ... 42 type Storage struct { 43 adaptors *adaptors.Adaptors 44 pool sync.Map 45 quit chan struct{} 46 inProcess *atomic.Bool 47 isStarted *atomic.Bool 48 eventBus bus.Bus 49 } 50 51 // NewStorage ... 52 func NewStorage( 53 adaptors *adaptors.Adaptors, 54 eventBus bus.Bus) *Storage { 55 storage := &Storage{ 56 adaptors: adaptors, 57 pool: sync.Map{}, 58 quit: make(chan struct{}), 59 inProcess: atomic.NewBool(false), 60 isStarted: atomic.NewBool(true), 61 eventBus: eventBus, 62 } 63 64 go func() { 65 ticker := time.NewTicker(time.Minute * 1) 66 defer ticker.Stop() 67 68 for { 69 select { 70 case <-ticker.C: 71 storage.serialize() 72 case <-storage.quit: 73 return 74 } 75 } 76 }() 77 78 return storage 79 } 80 81 // Shutdown ... 82 func (s *Storage) Shutdown() { 83 if !s.isStarted.CompareAndSwap(true, false) { 84 return 85 } 86 close(s.quit) 87 s.serialize() 88 } 89 90 // Search ... 91 func (s *Storage) Search(name string) (result map[string]string) { 92 return s.search(name) 93 } 94 95 // Push ... 96 func (s *Storage) Push(name string, v string) (err error) { 97 err = s.push(name, v) 98 return 99 } 100 101 // GetByName ... 102 func (s *Storage) GetByName(name string) (val string, err error) { 103 return s.getByName(name) 104 } 105 106 // Pop ... 107 func (s *Storage) Pop(name string) (val string, err error) { 108 return s.pop(name) 109 } 110 111 func (s *Storage) push(name string, v string) (err error) { 112 s.pool.Store(name, m.Variable{ 113 Name: name, 114 Changed: true, 115 Value: v, 116 }) 117 return 118 } 119 120 func (s *Storage) getByName(name string) (val string, err error) { 121 122 if v, ok := s.pool.Load(name); ok { 123 val = v.(m.Variable).Value 124 return 125 } 126 var storage m.Variable 127 if storage, err = s.adaptors.Variable.GetByName(context.Background(), name); err != nil { 128 return 129 } 130 val = storage.Value 131 132 return 133 } 134 135 func (s *Storage) pop(name string) (val string, err error) { 136 val, err = s.getByName(name) 137 if err != nil { 138 return 139 } 140 if err = s.adaptors.Variable.Delete(context.Background(), name); err != nil { 141 return 142 } 143 s.pool.Delete(name) 144 return 145 } 146 147 // Serialize ... 148 func (s *Storage) Serialize() { 149 s.serialize() 150 } 151 152 func (s *Storage) serialize() { 153 154 if !s.inProcess.CompareAndSwap(false, true) { 155 return 156 } 157 defer s.inProcess.Store(false) 158 159 var data m.Variable 160 var ok bool 161 162 s.pool.Range(func(key, val interface{}) bool { 163 data, ok = val.(m.Variable) 164 if !ok { 165 return true 166 } 167 168 if !data.Changed { 169 return true 170 } 171 172 data.Changed = false 173 174 s.pool.Store(key, data) 175 176 if err := s.adaptors.Variable.CreateOrUpdate(context.Background(), data); err != nil { 177 log.Error(err.Error()) 178 return true 179 } 180 s.eventBus.Publish(fmt.Sprintf("system/models/variables/%s", data.Name), events.EventUpdatedVariableModel{ 181 Name: data.Name, 182 Value: data.Value, 183 }) 184 185 return true 186 }) 187 188 } 189 190 func (s *Storage) search(sub string) (result map[string]string) { 191 result = make(map[string]string) 192 s.pool.Range(func(key, val interface{}) bool { 193 if strings.Contains(key.(string), sub) { 194 if data, ok := val.(m.Variable); ok { 195 result[data.Name] = data.Value 196 } 197 } 198 199 return true 200 }) 201 202 list, _, err := s.adaptors.Variable.Search(context.Background(), sub, 99, 0) 203 if err != nil { 204 return 205 } 206 for _, fromDb := range list { 207 if _, ok := result[fromDb.Name]; ok { 208 continue 209 } 210 result[fromDb.Name] = fromDb.Value 211 s.pool.Store(fromDb.Name, fromDb) 212 } 213 return 214 }