github.com/bchainhub/blockbook@v0.3.2/common/internalstate.go (about) 1 package common 2 3 import ( 4 "encoding/json" 5 "sort" 6 "sync" 7 "time" 8 ) 9 10 const ( 11 // DbStateClosed means db was closed gracefully 12 DbStateClosed = uint32(iota) 13 // DbStateOpen means db is open or application died without closing the db 14 DbStateOpen 15 // DbStateInconsistent means db is in inconsistent state and cannot be used 16 DbStateInconsistent 17 ) 18 19 // InternalStateColumn contains the data of a db column 20 type InternalStateColumn struct { 21 Name string `json:"name"` 22 Version uint32 `json:"version"` 23 Rows int64 `json:"rows"` 24 KeyBytes int64 `json:"keyBytes"` 25 ValueBytes int64 `json:"valueBytes"` 26 Updated time.Time `json:"updated"` 27 } 28 29 // InternalState contains the data of the internal state 30 type InternalState struct { 31 mux sync.Mutex 32 33 Coin string `json:"coin"` 34 CoinShortcut string `json:"coinShortcut"` 35 CoinLabel string `json:"coinLabel"` 36 Host string `json:"host"` 37 38 DbState uint32 `json:"dbState"` 39 40 LastStore time.Time `json:"lastStore"` 41 42 // true if application is with flag --sync 43 SyncMode bool `json:"syncMode"` 44 45 InitialSync bool `json:"initialSync"` 46 IsSynchronized bool `json:"isSynchronized"` 47 BestHeight uint32 `json:"bestHeight"` 48 LastSync time.Time `json:"lastSync"` 49 BlockTimes []uint32 `json:"-"` 50 51 IsMempoolSynchronized bool `json:"isMempoolSynchronized"` 52 MempoolSize int `json:"mempoolSize"` 53 LastMempoolSync time.Time `json:"lastMempoolSync"` 54 55 DbColumns []InternalStateColumn `json:"dbColumns"` 56 57 UtxoChecked bool `json:"utxoChecked"` 58 } 59 60 // StartedSync signals start of synchronization 61 func (is *InternalState) StartedSync() { 62 is.mux.Lock() 63 defer is.mux.Unlock() 64 is.IsSynchronized = false 65 } 66 67 // FinishedSync marks end of synchronization, bestHeight specifies new best block height 68 func (is *InternalState) FinishedSync(bestHeight uint32) { 69 is.mux.Lock() 70 defer is.mux.Unlock() 71 is.IsSynchronized = true 72 is.BestHeight = bestHeight 73 is.LastSync = time.Now() 74 } 75 76 // UpdateBestHeight sets new best height, without changing IsSynchronized flag 77 func (is *InternalState) UpdateBestHeight(bestHeight uint32) { 78 is.mux.Lock() 79 defer is.mux.Unlock() 80 is.BestHeight = bestHeight 81 is.LastSync = time.Now() 82 } 83 84 // FinishedSyncNoChange marks end of synchronization in case no index update was necessary, it does not update lastSync time 85 func (is *InternalState) FinishedSyncNoChange() { 86 is.mux.Lock() 87 defer is.mux.Unlock() 88 is.IsSynchronized = true 89 } 90 91 // GetSyncState gets the state of synchronization 92 func (is *InternalState) GetSyncState() (bool, uint32, time.Time) { 93 is.mux.Lock() 94 defer is.mux.Unlock() 95 return is.IsSynchronized, is.BestHeight, is.LastSync 96 } 97 98 // StartedMempoolSync signals start of mempool synchronization 99 func (is *InternalState) StartedMempoolSync() { 100 is.mux.Lock() 101 defer is.mux.Unlock() 102 is.IsMempoolSynchronized = false 103 } 104 105 // FinishedMempoolSync marks end of mempool synchronization 106 func (is *InternalState) FinishedMempoolSync(mempoolSize int) { 107 is.mux.Lock() 108 defer is.mux.Unlock() 109 is.IsMempoolSynchronized = true 110 is.MempoolSize = mempoolSize 111 is.LastMempoolSync = time.Now() 112 } 113 114 // GetMempoolSyncState gets the state of mempool synchronization 115 func (is *InternalState) GetMempoolSyncState() (bool, time.Time, int) { 116 is.mux.Lock() 117 defer is.mux.Unlock() 118 return is.IsMempoolSynchronized, is.LastMempoolSync, is.MempoolSize 119 } 120 121 // AddDBColumnStats adds differences in column statistics to column stats 122 func (is *InternalState) AddDBColumnStats(c int, rowsDiff int64, keyBytesDiff int64, valueBytesDiff int64) { 123 is.mux.Lock() 124 defer is.mux.Unlock() 125 dc := &is.DbColumns[c] 126 dc.Rows += rowsDiff 127 dc.KeyBytes += keyBytesDiff 128 dc.ValueBytes += valueBytesDiff 129 dc.Updated = time.Now() 130 } 131 132 // SetDBColumnStats sets new values of column stats 133 func (is *InternalState) SetDBColumnStats(c int, rows int64, keyBytes int64, valueBytes int64) { 134 is.mux.Lock() 135 defer is.mux.Unlock() 136 dc := &is.DbColumns[c] 137 dc.Rows = rows 138 dc.KeyBytes = keyBytes 139 dc.ValueBytes = valueBytes 140 dc.Updated = time.Now() 141 } 142 143 // GetDBColumnStatValues gets stat values for given column 144 func (is *InternalState) GetDBColumnStatValues(c int) (int64, int64, int64) { 145 is.mux.Lock() 146 defer is.mux.Unlock() 147 if c < len(is.DbColumns) { 148 return is.DbColumns[c].Rows, is.DbColumns[c].KeyBytes, is.DbColumns[c].ValueBytes 149 } 150 return 0, 0, 0 151 } 152 153 // GetAllDBColumnStats returns stats for all columns 154 func (is *InternalState) GetAllDBColumnStats() []InternalStateColumn { 155 is.mux.Lock() 156 defer is.mux.Unlock() 157 return append(is.DbColumns[:0:0], is.DbColumns...) 158 } 159 160 // DBSizeTotal sums the computed sizes of all columns 161 func (is *InternalState) DBSizeTotal() int64 { 162 is.mux.Lock() 163 defer is.mux.Unlock() 164 total := int64(0) 165 for _, c := range is.DbColumns { 166 total += c.KeyBytes + c.ValueBytes 167 } 168 return total 169 } 170 171 // GetBlockTime returns block time if block found or 0 172 func (is *InternalState) GetBlockTime(height uint32) uint32 { 173 is.mux.Lock() 174 defer is.mux.Unlock() 175 if int(height) < len(is.BlockTimes) { 176 return is.BlockTimes[height] 177 } 178 return 0 179 } 180 181 // AppendBlockTime appends block time to BlockTimes 182 func (is *InternalState) AppendBlockTime(time uint32) { 183 is.mux.Lock() 184 defer is.mux.Unlock() 185 is.BlockTimes = append(is.BlockTimes, time) 186 } 187 188 // RemoveLastBlockTimes removes last times from BlockTimes 189 func (is *InternalState) RemoveLastBlockTimes(count int) { 190 is.mux.Lock() 191 defer is.mux.Unlock() 192 if len(is.BlockTimes) < count { 193 count = len(is.BlockTimes) 194 } 195 is.BlockTimes = is.BlockTimes[:len(is.BlockTimes)-count] 196 } 197 198 // GetBlockHeightOfTime returns block height of the first block with time greater or equal to the given time or MaxUint32 if no such block 199 func (is *InternalState) GetBlockHeightOfTime(time uint32) uint32 { 200 is.mux.Lock() 201 defer is.mux.Unlock() 202 height := sort.Search(len(is.BlockTimes), func(i int) bool { return time <= is.BlockTimes[i] }) 203 if height == len(is.BlockTimes) { 204 return ^uint32(0) 205 } 206 // as the block times can sometimes be out of order try 20 blocks lower to locate a block with the time greater or equal to the given time 207 max, height := height, height-20 208 if height < 0 { 209 height = 0 210 } 211 for ; height <= max; height++ { 212 if time <= is.BlockTimes[height] { 213 break 214 } 215 } 216 return uint32(height) 217 } 218 219 // Pack marshals internal state to json 220 func (is *InternalState) Pack() ([]byte, error) { 221 is.mux.Lock() 222 defer is.mux.Unlock() 223 is.LastStore = time.Now() 224 return json.Marshal(is) 225 } 226 227 // UnpackInternalState unmarshals internal state from json 228 func UnpackInternalState(buf []byte) (*InternalState, error) { 229 var is InternalState 230 if err := json.Unmarshal(buf, &is); err != nil { 231 return nil, err 232 } 233 return &is, nil 234 }