github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/shed/db.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:44</date> 10 //</624450117457416192> 11 12 13 //包棚提供了一个简单的抽象组件来组成 14 //对按字段和索引组织的存储数据进行更复杂的操作。 15 // 16 //唯一保存有关群存储块数据的逻辑信息的类型 17 //元数据是项。这一部分主要不是为了 18 //性能原因。 19 package shed 20 21 import ( 22 "errors" 23 "fmt" 24 "strconv" 25 "strings" 26 "time" 27 28 "github.com/ethereum/go-ethereum/metrics" 29 "github.com/ethereum/go-ethereum/swarm/log" 30 "github.com/syndtr/goleveldb/leveldb" 31 "github.com/syndtr/goleveldb/leveldb/iterator" 32 "github.com/syndtr/goleveldb/leveldb/opt" 33 ) 34 35 const ( 36 openFileLimit = 128 //级别ldb openfilescachecapacity的限制。 37 writePauseWarningThrottler = 1 * time.Minute 38 ) 39 40 //DB在级别数据库上提供抽象,以便 41 //使用字段和有序索引实现复杂结构。 42 //它提供了一个模式功能来存储字段和索引 43 //有关命名和类型的信息。 44 type DB struct { 45 ldb *leveldb.DB 46 47 compTimeMeter metrics.Meter //用于测量数据库压缩所花费的总时间的仪表 48 compReadMeter metrics.Meter //测量压实过程中读取数据的仪表 49 compWriteMeter metrics.Meter //测量压实过程中写入数据的仪表 50 writeDelayNMeter metrics.Meter //用于测量数据库压缩导致的写入延迟数的仪表 51 writeDelayMeter metrics.Meter //用于测量数据库压缩导致的写入延迟持续时间的仪表 52 diskReadMeter metrics.Meter //测量有效数据读取量的仪表 53 diskWriteMeter metrics.Meter //测量写入数据有效量的仪表 54 55 quitChan chan chan error //退出通道以在关闭数据库之前停止度量集合 56 } 57 58 //new db构造新的db并验证架构 59 //如果它存在于给定路径上的数据库中。 60 //metricsPrefix用于为给定数据库收集度量。 61 func NewDB(path string, metricsPrefix string) (db *DB, err error) { 62 ldb, err := leveldb.OpenFile(path, &opt.Options{ 63 OpenFilesCacheCapacity: openFileLimit, 64 }) 65 if err != nil { 66 return nil, err 67 } 68 db = &DB{ 69 ldb: ldb, 70 } 71 72 if _, err = db.getSchema(); err != nil { 73 if err == leveldb.ErrNotFound { 74 //用已初始化的默认字段保存架构 75 if err = db.putSchema(schema{ 76 Fields: make(map[string]fieldSpec), 77 Indexes: make(map[byte]indexSpec), 78 }); err != nil { 79 return nil, err 80 } 81 } else { 82 return nil, err 83 } 84 } 85 86 //为DB配置仪表 87 db.configure(metricsPrefix) 88 89 //为定期度量收集器创建退出通道并运行它 90 db.quitChan = make(chan chan error) 91 92 go db.meter(10 * time.Second) 93 94 return db, nil 95 } 96 97 //将Wrapps-LevelDB-Put方法添加到增量度量计数器。 98 func (db *DB) Put(key []byte, value []byte) (err error) { 99 err = db.ldb.Put(key, value, nil) 100 if err != nil { 101 metrics.GetOrRegisterCounter("DB.putFail", nil).Inc(1) 102 return err 103 } 104 metrics.GetOrRegisterCounter("DB.put", nil).Inc(1) 105 return nil 106 } 107 108 //get wrapps leveldb get方法以增加度量计数器。 109 func (db *DB) Get(key []byte) (value []byte, err error) { 110 value, err = db.ldb.Get(key, nil) 111 if err != nil { 112 if err == leveldb.ErrNotFound { 113 metrics.GetOrRegisterCounter("DB.getNotFound", nil).Inc(1) 114 } else { 115 metrics.GetOrRegisterCounter("DB.getFail", nil).Inc(1) 116 } 117 return nil, err 118 } 119 metrics.GetOrRegisterCounter("DB.get", nil).Inc(1) 120 return value, nil 121 } 122 123 //DELETE包装级别DB DELETE方法以增加度量计数器。 124 func (db *DB) Delete(key []byte) (err error) { 125 err = db.ldb.Delete(key, nil) 126 if err != nil { 127 metrics.GetOrRegisterCounter("DB.deleteFail", nil).Inc(1) 128 return err 129 } 130 metrics.GetOrRegisterCounter("DB.delete", nil).Inc(1) 131 return nil 132 } 133 134 //NewIterator将LevelDB NewIterator方法包装为增量度量计数器。 135 func (db *DB) NewIterator() iterator.Iterator { 136 metrics.GetOrRegisterCounter("DB.newiterator", nil).Inc(1) 137 138 return db.ldb.NewIterator(nil, nil) 139 } 140 141 //WRITEBATCH将LEVELDB WRITE方法包装为增量度量计数器。 142 func (db *DB) WriteBatch(batch *leveldb.Batch) (err error) { 143 err = db.ldb.Write(batch, nil) 144 if err != nil { 145 metrics.GetOrRegisterCounter("DB.writebatchFail", nil).Inc(1) 146 return err 147 } 148 metrics.GetOrRegisterCounter("DB.writebatch", nil).Inc(1) 149 return nil 150 } 151 152 //关闭关闭级别数据库。 153 func (db *DB) Close() (err error) { 154 close(db.quitChan) 155 return db.ldb.Close() 156 } 157 158 //配置配置数据库度量收集器 159 func (db *DB) configure(prefix string) { 160 //在请求的前缀处初始化所有度量收集器 161 db.compTimeMeter = metrics.NewRegisteredMeter(prefix+"compact/time", nil) 162 db.compReadMeter = metrics.NewRegisteredMeter(prefix+"compact/input", nil) 163 db.compWriteMeter = metrics.NewRegisteredMeter(prefix+"compact/output", nil) 164 db.diskReadMeter = metrics.NewRegisteredMeter(prefix+"disk/read", nil) 165 db.diskWriteMeter = metrics.NewRegisteredMeter(prefix+"disk/write", nil) 166 db.writeDelayMeter = metrics.NewRegisteredMeter(prefix+"compact/writedelay/duration", nil) 167 db.writeDelayNMeter = metrics.NewRegisteredMeter(prefix+"compact/writedelay/counter", nil) 168 } 169 170 func (db *DB) meter(refresh time.Duration) { 171 //创建计数器以存储当前和以前的压缩值 172 compactions := make([][]float64, 2) 173 for i := 0; i < 2; i++ { 174 compactions[i] = make([]float64, 3) 175 } 176 //为iostats创建存储。 177 var iostats [2]float64 178 179 //为写入延迟创建存储和警告日志跟踪程序。 180 var ( 181 delaystats [2]int64 182 lastWritePaused time.Time 183 ) 184 185 var ( 186 errc chan error 187 merr error 188 ) 189 190 //无限迭代并收集统计信息 191 for i := 1; errc == nil && merr == nil; i++ { 192 //检索数据库状态 193 stats, err := db.ldb.GetProperty("leveldb.stats") 194 if err != nil { 195 log.Error("Failed to read database stats", "err", err) 196 merr = err 197 continue 198 } 199 //找到压缩表,跳过标题 200 lines := strings.Split(stats, "\n") 201 for len(lines) > 0 && strings.TrimSpace(lines[0]) != "Compactions" { 202 lines = lines[1:] 203 } 204 if len(lines) <= 3 { 205 log.Error("Compaction table not found") 206 merr = errors.New("compaction table not found") 207 continue 208 } 209 lines = lines[3:] 210 211 //遍历所有表行,并累积条目 212 for j := 0; j < len(compactions[i%2]); j++ { 213 compactions[i%2][j] = 0 214 } 215 for _, line := range lines { 216 parts := strings.Split(line, "|") 217 if len(parts) != 6 { 218 break 219 } 220 for idx, counter := range parts[3:] { 221 value, err := strconv.ParseFloat(strings.TrimSpace(counter), 64) 222 if err != nil { 223 log.Error("Compaction entry parsing failed", "err", err) 224 merr = err 225 continue 226 } 227 compactions[i%2][idx] += value 228 } 229 } 230 //更新所有要求的仪表 231 if db.compTimeMeter != nil { 232 db.compTimeMeter.Mark(int64((compactions[i%2][0] - compactions[(i-1)%2][0]) * 1000 * 1000 * 1000)) 233 } 234 if db.compReadMeter != nil { 235 db.compReadMeter.Mark(int64((compactions[i%2][1] - compactions[(i-1)%2][1]) * 1024 * 1024)) 236 } 237 if db.compWriteMeter != nil { 238 db.compWriteMeter.Mark(int64((compactions[i%2][2] - compactions[(i-1)%2][2]) * 1024 * 1024)) 239 } 240 241 //检索写入延迟统计信息 242 writedelay, err := db.ldb.GetProperty("leveldb.writedelay") 243 if err != nil { 244 log.Error("Failed to read database write delay statistic", "err", err) 245 merr = err 246 continue 247 } 248 var ( 249 delayN int64 250 delayDuration string 251 duration time.Duration 252 paused bool 253 ) 254 if n, err := fmt.Sscanf(writedelay, "DelayN:%d Delay:%s Paused:%t", &delayN, &delayDuration, &paused); n != 3 || err != nil { 255 log.Error("Write delay statistic not found") 256 merr = err 257 continue 258 } 259 duration, err = time.ParseDuration(delayDuration) 260 if err != nil { 261 log.Error("Failed to parse delay duration", "err", err) 262 merr = err 263 continue 264 } 265 if db.writeDelayNMeter != nil { 266 db.writeDelayNMeter.Mark(delayN - delaystats[0]) 267 } 268 if db.writeDelayMeter != nil { 269 db.writeDelayMeter.Mark(duration.Nanoseconds() - delaystats[1]) 270 } 271 //如果显示了数据库正在执行压缩的警告,则 272 //警告将被保留一分钟,以防压倒用户。 273 if paused && delayN-delaystats[0] == 0 && duration.Nanoseconds()-delaystats[1] == 0 && 274 time.Now().After(lastWritePaused.Add(writePauseWarningThrottler)) { 275 log.Warn("Database compacting, degraded performance") 276 lastWritePaused = time.Now() 277 } 278 delaystats[0], delaystats[1] = delayN, duration.Nanoseconds() 279 280 //检索数据库iostats。 281 ioStats, err := db.ldb.GetProperty("leveldb.iostats") 282 if err != nil { 283 log.Error("Failed to read database iostats", "err", err) 284 merr = err 285 continue 286 } 287 var nRead, nWrite float64 288 parts := strings.Split(ioStats, " ") 289 if len(parts) < 2 { 290 log.Error("Bad syntax of ioStats", "ioStats", ioStats) 291 merr = fmt.Errorf("bad syntax of ioStats %s", ioStats) 292 continue 293 } 294 if n, err := fmt.Sscanf(parts[0], "Read(MB):%f", &nRead); n != 1 || err != nil { 295 log.Error("Bad syntax of read entry", "entry", parts[0]) 296 merr = err 297 continue 298 } 299 if n, err := fmt.Sscanf(parts[1], "Write(MB):%f", &nWrite); n != 1 || err != nil { 300 log.Error("Bad syntax of write entry", "entry", parts[1]) 301 merr = err 302 continue 303 } 304 if db.diskReadMeter != nil { 305 db.diskReadMeter.Mark(int64((nRead - iostats[0]) * 1024 * 1024)) 306 } 307 if db.diskWriteMeter != nil { 308 db.diskWriteMeter.Mark(int64((nWrite - iostats[1]) * 1024 * 1024)) 309 } 310 iostats[0], iostats[1] = nRead, nWrite 311 312 //睡一会儿,然后重复统计数据收集 313 select { 314 case errc = <-db.quitChan: 315 //停止请求,停止锤击数据库 316 case <-time.After(refresh): 317 //超时,收集一组新的统计信息 318 } 319 } 320 321 if errc == nil { 322 errc = <-db.quitChan 323 } 324 errc <- merr 325 } 326