github.com/turingchain2020/turingchain@v1.1.21/common/db/go_pegasus.go (about) 1 // Copyright Turing Corp. 2018 All Rights Reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package db 6 7 import ( 8 "bytes" 9 "context" 10 "strings" 11 "time" 12 13 log "github.com/turingchain2020/turingchain/common/log/log15" 14 "github.com/turingchain2020/turingchain/types" 15 "github.com/XiaoMi/pegasus-go-client/pegasus" 16 "github.com/syndtr/goleveldb/leveldb/util" 17 ) 18 19 var slog = log.New("module", "db.pegasus") 20 var pdbBench = &SsdbBench{} 21 22 //HashKeyLen hash长度 23 var HashKeyLen = 24 24 25 func init() { 26 dbCreator := func(name string, dir string, cache int) (DB, error) { 27 return NewPegasusDB(name, dir, cache) 28 } 29 registerDBCreator(goPegasusDbBackendStr, dbCreator, false) 30 } 31 32 //PegasusDB db 33 type PegasusDB struct { 34 BaseDB 35 cfg *pegasus.Config 36 name string 37 client pegasus.Client 38 table pegasus.TableConnector 39 } 40 41 func printPegasusBenchmark() { 42 tick := time.Tick(time.Minute * 5) 43 for { 44 <-tick 45 slog.Info(pdbBench.String()) 46 } 47 } 48 49 //NewPegasusDB new 50 func NewPegasusDB(name string, dir string, cache int) (*PegasusDB, error) { 51 database := &PegasusDB{name: name} 52 database.cfg = parsePegasusNodes(dir) 53 54 if database.cfg == nil { 55 slog.Error("no valid instance exists, exit!") 56 return nil, types.ErrDataBaseDamage 57 } 58 var err error 59 database.client = pegasus.NewClient(*database.cfg) 60 tb, err := database.client.OpenTable(context.Background(), database.name) 61 if err != nil { 62 slog.Error("connect to pegasus error!", "pegasus", database.cfg, "error", err) 63 err = database.client.Close() 64 if err != nil { 65 slog.Error("database.client", "close err", err) 66 } 67 return nil, types.ErrDataBaseDamage 68 } 69 database.table = tb 70 71 go printPegasusBenchmark() 72 return database, nil 73 } 74 75 // url pattern: ip:port,ip:port 76 func parsePegasusNodes(url string) *pegasus.Config { 77 hosts := strings.Split(url, ",") 78 if hosts == nil { 79 slog.Error("invalid url") 80 return nil 81 } 82 83 cfg := &pegasus.Config{MetaServers: hosts} 84 return cfg 85 } 86 87 //Get get 88 func (db *PegasusDB) Get(key []byte) ([]byte, error) { 89 start := time.Now() 90 hashKey := getHashKey(key) 91 value, err := db.table.Get(context.Background(), hashKey, key) 92 if err != nil { 93 //slog.Error("Get value error", "error", err, "key", key, "keyhex", hex.EncodeToString(key), "keystr", string(key)) 94 return nil, err 95 } 96 if value == nil { 97 return nil, ErrNotFoundInDb 98 } 99 100 pdbBench.read(1, time.Since(start)) 101 return value, nil 102 } 103 104 //Set set 105 func (db *PegasusDB) Set(key []byte, value []byte) error { 106 start := time.Now() 107 hashKey := getHashKey(key) 108 err := db.table.Set(context.Background(), hashKey, key, value) 109 if err != nil { 110 slog.Error("Set", "error", err) 111 return err 112 } 113 pdbBench.write(1, time.Since(start)) 114 return nil 115 } 116 117 //SetSync 设置同步 118 func (db *PegasusDB) SetSync(key []byte, value []byte) error { 119 return db.Set(key, value) 120 } 121 122 //Delete 删除 123 func (db *PegasusDB) Delete(key []byte) error { 124 start := time.Now() 125 defer pdbBench.write(1, time.Since(start)) 126 hashKey := getHashKey(key) 127 err := db.table.Del(context.Background(), hashKey, key) 128 if err != nil { 129 slog.Error("Delete", "error", err) 130 return err 131 } 132 return nil 133 } 134 135 //DeleteSync 删除同步 136 func (db *PegasusDB) DeleteSync(key []byte) error { 137 return db.Delete(key) 138 } 139 140 //Close 同步 141 func (db *PegasusDB) Close() { 142 err := db.table.Close() 143 if err != nil { 144 llog.Error("Close", "db table error", err) 145 } 146 err = db.client.Close() 147 if err != nil { 148 llog.Error("Close", "client error", err) 149 } 150 } 151 152 //Print 打印 153 func (db *PegasusDB) Print() { 154 } 155 156 //Stats ... 157 func (db *PegasusDB) Stats() map[string]string { 158 return nil 159 } 160 161 //Iterator 迭代器 162 func (db *PegasusDB) Iterator(begin []byte, end []byte, reverse bool) Iterator { 163 var ( 164 err error 165 vals []*pegasus.KeyValue 166 start []byte 167 over []byte 168 ) 169 if end == nil { 170 end = bytesPrefix(begin) 171 } 172 if bytes.Equal(end, types.EmptyValue) { 173 end = nil 174 } 175 limit := util.Range{Start: begin, Limit: end} 176 hashKey := getHashKey(begin) 177 178 if reverse { 179 start = begin 180 over = limit.Limit 181 } else { 182 start = limit.Limit 183 over = begin 184 } 185 dbit := &PegasusIt{itBase: itBase{begin, end, reverse}, index: -1, table: db.table, itbegin: start, itend: over} 186 opts := &pegasus.MultiGetOptions{StartInclusive: false, StopInclusive: false, MaxFetchCount: IteratorPageSize, Reverse: dbit.reverse} 187 vals, _, err = db.table.MultiGetRangeOpt(context.Background(), hashKey, begin, limit.Limit, opts) 188 if err != nil { 189 slog.Error("create iterator error!") 190 return nil 191 } 192 if len(vals) > 0 { 193 dbit.vals = vals 194 // 如果返回的数据大小刚好满足分页,则假设下一页还有数据 195 if len(dbit.vals) == IteratorPageSize { 196 dbit.nextPage = true 197 // 下一页数据的开始,等于本页数据的结束,不过在下次查询时需要设置StartInclusiv=false,因为本条数据已经包含 198 dbit.tmpEnd = dbit.vals[IteratorPageSize-1].SortKey 199 } 200 } 201 return dbit 202 } 203 204 //PegasusIt ... 205 type PegasusIt struct { 206 itBase 207 table pegasus.TableConnector 208 vals []*pegasus.KeyValue 209 index int 210 nextPage bool 211 tmpEnd []byte 212 213 // 迭代开始位置 214 itbegin []byte 215 // 迭代结束位置 216 itend []byte 217 // 当前所属的页数(从0开始) 218 pageNo int 219 } 220 221 //Close 关闭 222 func (dbit *PegasusIt) Close() { 223 dbit.index = -1 224 } 225 226 //Next next 227 func (dbit *PegasusIt) Next() bool { 228 if len(dbit.vals) > dbit.index+1 { 229 dbit.index++ 230 return true 231 } 232 // 如果有下一页数据,则自动抓取 233 if dbit.nextPage { 234 return dbit.cacheNextPage(dbit.tmpEnd) 235 } 236 return false 237 238 } 239 240 func (dbit *PegasusIt) initPage(begin, end []byte) bool { 241 var ( 242 vals []*pegasus.KeyValue 243 err error 244 ) 245 opts := &pegasus.MultiGetOptions{StartInclusive: false, StopInclusive: false, MaxFetchCount: IteratorPageSize, Reverse: dbit.reverse} 246 hashKey := getHashKey(begin) 247 vals, _, err = dbit.table.MultiGetRangeOpt(context.Background(), hashKey, begin, end, opts) 248 249 if err != nil { 250 slog.Error("get iterator next page error", "error", err, "begin", begin, "end", dbit.itend, "reverse", dbit.reverse) 251 return false 252 } 253 254 if len(vals) > 0 { 255 // 这里只改变keys,不改变index 256 dbit.vals = vals 257 258 // 如果返回的数据大小刚好满足分页,则假设下一页还有数据 259 if len(vals) == IteratorPageSize { 260 dbit.nextPage = true 261 dbit.tmpEnd = dbit.vals[IteratorPageSize-1].SortKey 262 } else { 263 dbit.nextPage = false 264 } 265 return true 266 } 267 return false 268 269 } 270 271 // 获取下一页的数据 272 func (dbit *PegasusIt) cacheNextPage(flag []byte) bool { 273 var ( 274 over []byte 275 ) 276 // 如果是逆序,则取从开始到flag的数据 277 if dbit.reverse { 278 over = dbit.itbegin 279 } else { 280 over = dbit.itend 281 } 282 // 如果是正序,则取从flag到结束的数据 283 if dbit.initPage(flag, over) { 284 dbit.index = 0 285 dbit.pageNo++ 286 return true 287 } 288 return false 289 290 } 291 292 func (dbit *PegasusIt) checkKeyCmp(key1, key2 []byte, reverse bool) bool { 293 if reverse { 294 return bytes.Compare(key1, key2) < 0 295 } 296 return bytes.Compare(key1, key2) > 0 297 } 298 299 func (dbit *PegasusIt) findInPage(key []byte) int { 300 pos := -1 301 for i, v := range dbit.vals { 302 if i < dbit.index { 303 continue 304 } 305 if dbit.checkKeyCmp(key, v.SortKey, dbit.reverse) { 306 continue 307 } else { 308 pos = i 309 break 310 } 311 } 312 return pos 313 } 314 315 //Seek 查找 316 func (dbit *PegasusIt) Seek(key []byte) bool { 317 pos := dbit.findInPage(key) 318 319 // 如果第一页已经找到,不会走入此逻辑 320 for pos == -1 && dbit.nextPage { 321 if dbit.cacheNextPage(dbit.tmpEnd) { 322 pos = dbit.findInPage(key) 323 } else { 324 break 325 } 326 } 327 328 dbit.index = pos 329 return dbit.Valid() 330 } 331 332 //Rewind 从头开始 333 func (dbit *PegasusIt) Rewind() bool { 334 // 目前代码的Rewind调用都是在第一页,正常情况下走不到else分支; 335 // 但为了代码健壮性考虑,这里增加对else分支的处理 336 if dbit.pageNo == 0 { 337 dbit.index = 0 338 return true 339 } 340 341 // 当数据取到第N页的情况时,Rewind需要返回到第一页第一条 342 if dbit.initPage(dbit.itbegin, dbit.itend) { 343 dbit.index = 0 344 dbit.pageNo = 0 345 return true 346 } 347 return false 348 349 } 350 351 //Key key 352 func (dbit *PegasusIt) Key() []byte { 353 if dbit.index >= 0 && dbit.index < len(dbit.vals) { 354 return dbit.vals[dbit.index].SortKey 355 } 356 return nil 357 358 } 359 360 //Value value 361 func (dbit *PegasusIt) Value() []byte { 362 if dbit.index >= len(dbit.vals) { 363 slog.Error("get iterator value error: index out of bounds") 364 return nil 365 } 366 367 return dbit.vals[dbit.index].Value 368 } 369 370 func (dbit *PegasusIt) Error() error { 371 return nil 372 } 373 374 //ValueCopy 复制 375 func (dbit *PegasusIt) ValueCopy() []byte { 376 v := dbit.Value() 377 value := make([]byte, len(v)) 378 copy(value, v) 379 return value 380 } 381 382 //Valid 合法性 383 func (dbit *PegasusIt) Valid() bool { 384 start := time.Now() 385 if dbit.index < 0 { 386 return false 387 } 388 if len(dbit.vals) <= dbit.index { 389 return false 390 } 391 key := dbit.vals[dbit.index].SortKey 392 pdbBench.read(1, time.Since(start)) 393 return dbit.checkKey(key) 394 } 395 396 //PegasusBatch batch 397 type PegasusBatch struct { 398 table pegasus.TableConnector 399 batchset map[string][]byte 400 batchdel map[string][]byte 401 size int 402 } 403 404 //NewBatch new 405 func (db *PegasusDB) NewBatch(sync bool) Batch { 406 return &PegasusBatch{table: db.table, batchset: make(map[string][]byte), batchdel: make(map[string][]byte)} 407 } 408 409 //Set set 410 func (db *PegasusBatch) Set(key, value []byte) { 411 db.batchset[string(key)] = value 412 delete(db.batchdel, string(key)) 413 db.size += len(value) 414 db.size += len(key) 415 } 416 417 //Delete 删除 418 func (db *PegasusBatch) Delete(key []byte) { 419 db.batchset[string(key)] = []byte("") 420 delete(db.batchset, string(key)) 421 db.batchdel[string(key)] = key 422 db.size += len(key) 423 } 424 425 // 注意本方法的实现逻辑,因为ssdb没有提供删除和更新同时进行的批量操作; 426 // 所以这里先执行更新操作(删除的KEY在这里会将VALUE设置为空); 427 // 然后再执行删除操作; 428 // 这样即使中间执行出错,也不会导致删除结果未写入的情况(值已经被置空); 429 func (db *PegasusBatch) Write() error { 430 start := time.Now() 431 432 // 这里其实也需要对hashKey进行分别计算,然后分组查询,最后汇总结果 433 if len(db.batchset) > 0 { 434 var ( 435 keysMap map[string][][]byte 436 valsMap map[string][][]byte 437 hashKey []byte 438 byteKey []byte 439 keys [][]byte 440 values [][]byte 441 ) 442 keysMap = make(map[string][][]byte) 443 valsMap = make(map[string][][]byte) 444 445 // 首先,使用hashKey进行数据分组 446 for k, v := range db.batchset { 447 byteKey = []byte(k) 448 hashKey = getHashKey(byteKey) 449 if value, ok := keysMap[string(hashKey)]; ok { 450 keysMap[string(hashKey)] = append(value, byteKey) 451 valsMap[string(hashKey)] = append(valsMap[string(hashKey)], v) 452 } else { 453 keysMap[string(hashKey)] = [][]byte{byteKey} 454 valsMap[string(hashKey)] = [][]byte{v} 455 } 456 } 457 // 然后,再分别提交修改 458 for k, v := range keysMap { 459 keys = v 460 values = valsMap[k] 461 462 err := db.table.MultiSet(context.Background(), []byte(k), keys, values) 463 if err != nil { 464 slog.Error("Write (multi_set)", "error", err) 465 return err 466 } 467 } 468 } 469 470 if len(db.batchdel) > 0 { 471 var ( 472 keysMap map[string][][]byte 473 hashKey []byte 474 byteKey []byte 475 ) 476 keysMap = make(map[string][][]byte) 477 478 // 首先,使用hashKey进行数据分组 479 for k := range db.batchdel { 480 byteKey = []byte(k) 481 hashKey = getHashKey(byteKey) 482 if value, ok := keysMap[string(hashKey)]; ok { 483 keysMap[string(hashKey)] = append(value, byteKey) 484 } else { 485 keysMap[string(hashKey)] = [][]byte{byteKey} 486 } 487 } 488 489 // 然后,再分别提交删除 490 for k, v := range keysMap { 491 err := db.table.MultiDel(context.Background(), []byte(k), v) 492 if err != nil { 493 slog.Error("Write (multi_del)", "error", err) 494 return err 495 } 496 } 497 } 498 499 pdbBench.write(len(db.batchset)+len(db.batchdel), time.Since(start)) 500 return nil 501 } 502 503 //ValueSize value批长度 504 func (db *PegasusBatch) ValueSize() int { 505 return db.size 506 } 507 508 //ValueLen batch数量 509 func (db *PegasusBatch) ValueLen() int { 510 return len(db.batchset) 511 } 512 513 //Reset 重置 514 func (db *PegasusBatch) Reset() { 515 db.batchset = make(map[string][]byte) 516 db.batchdel = make(map[string][]byte) 517 db.size = 0 518 } 519 520 // UpdateWriteSync ... 521 func (db *PegasusBatch) UpdateWriteSync(sync bool) { 522 } 523 524 func getHashKey(key []byte) []byte { 525 if len(key) <= HashKeyLen { 526 return key 527 } 528 return key[:HashKeyLen] 529 }