github.com/ledgerwatch/erigon-lib@v1.0.0/kv/remotedbserver/remotedbserver.go (about) 1 /* 2 Copyright 2021 Erigon contributors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package remotedbserver 18 19 import ( 20 "context" 21 "encoding/base64" 22 "errors" 23 "fmt" 24 "io" 25 "reflect" 26 "sync" 27 "sync/atomic" 28 "time" 29 30 "github.com/ledgerwatch/log/v3" 31 "google.golang.org/protobuf/proto" 32 "google.golang.org/protobuf/types/known/emptypb" 33 34 "github.com/ledgerwatch/erigon-lib/common" 35 "github.com/ledgerwatch/erigon-lib/common/dbg" 36 "github.com/ledgerwatch/erigon-lib/common/hexutility" 37 "github.com/ledgerwatch/erigon-lib/gointerfaces/remote" 38 "github.com/ledgerwatch/erigon-lib/gointerfaces/types" 39 "github.com/ledgerwatch/erigon-lib/kv" 40 "github.com/ledgerwatch/erigon-lib/kv/iter" 41 "github.com/ledgerwatch/erigon-lib/kv/order" 42 ) 43 44 // MaxTxTTL - kv interface provide high-consistancy guaranties: Serializable Isolations Level https://en.wikipedia.org/wiki/Isolation_(database_systems) 45 // But it comes with cost: DB will start grow if run too long read transactions (hours) 46 // We decided limit TTL of transaction to `MaxTxTTL` 47 // 48 // It means you sill have `Serializable` if tx living < `MaxTxTTL` 49 // You start have Read Committed Level if tx living > `MaxTxTTL` 50 // 51 // It's done by `renew` method: after `renew` call reader will see all changes committed after last `renew` call. 52 // 53 // Erigon has much Historical data - which is immutable: reading of historical data for hours still gives you consistant data. 54 const MaxTxTTL = 60 * time.Second 55 56 // KvServiceAPIVersion - use it to track changes in API 57 // 1.1.0 - added pending transactions, add methods eth_getRawTransactionByHash, eth_retRawTransactionByBlockHashAndIndex, eth_retRawTransactionByBlockNumberAndIndex| Yes | | 58 // 1.2.0 - Added separated services for mining and txpool methods 59 // 2.0.0 - Rename all buckets 60 // 3.0.0 - ?? 61 // 4.0.0 - Server send tx.ViewID() after open tx 62 // 5.0 - BlockTransaction table now has canonical ids (txs of non-canonical blocks moving to NonCanonicalTransaction table) 63 // 5.1.0 - Added blockGasLimit to the StateChangeBatch 64 // 6.0.0 - Blocks now have system-txs - in the begin/end of block 65 // 6.1.0 - Add methods Range, IndexRange, HistoryGet, HistoryRange 66 // 6.2.0 - Add HistoryFiles to reply of Snapshots() method 67 var KvServiceAPIVersion = &types.VersionReply{Major: 6, Minor: 2, Patch: 0} 68 69 type KvServer struct { 70 remote.UnimplementedKVServer // must be embedded to have forward compatible implementations. 71 72 kv kv.RoDB 73 stateChangeStreams *StateChangePubSub 74 blockSnapshots Snapsthots 75 historySnapshots Snapsthots 76 ctx context.Context 77 78 //v3 fields 79 txIdGen atomic.Uint64 80 txsMapLock *sync.RWMutex 81 txs map[uint64]*threadSafeTx 82 83 trace bool 84 rangeStep int // make sure `s.with` has limited time 85 logger log.Logger 86 } 87 88 type threadSafeTx struct { 89 kv.Tx 90 sync.Mutex 91 } 92 93 type Snapsthots interface { 94 Files() []string 95 } 96 97 func NewKvServer(ctx context.Context, db kv.RoDB, snapshots Snapsthots, historySnapshots Snapsthots, logger log.Logger) *KvServer { 98 return &KvServer{ 99 trace: false, 100 rangeStep: 1024, 101 kv: db, stateChangeStreams: newStateChangeStreams(), ctx: ctx, 102 blockSnapshots: snapshots, historySnapshots: historySnapshots, 103 txs: map[uint64]*threadSafeTx{}, txsMapLock: &sync.RWMutex{}, 104 logger: logger, 105 } 106 } 107 108 // Version returns the service-side interface version number 109 func (s *KvServer) Version(context.Context, *emptypb.Empty) (*types.VersionReply, error) { 110 dbSchemaVersion := &kv.DBSchemaVersion 111 if KvServiceAPIVersion.Major > dbSchemaVersion.Major { 112 return KvServiceAPIVersion, nil 113 } 114 if dbSchemaVersion.Major > KvServiceAPIVersion.Major { 115 return dbSchemaVersion, nil 116 } 117 if KvServiceAPIVersion.Minor > dbSchemaVersion.Minor { 118 return KvServiceAPIVersion, nil 119 } 120 if dbSchemaVersion.Minor > KvServiceAPIVersion.Minor { 121 return dbSchemaVersion, nil 122 } 123 return dbSchemaVersion, nil 124 } 125 126 func (s *KvServer) begin(ctx context.Context) (id uint64, err error) { 127 if s.trace { 128 s.logger.Info(fmt.Sprintf("[kv_server] begin %d %s\n", id, dbg.Stack())) 129 } 130 s.txsMapLock.Lock() 131 defer s.txsMapLock.Unlock() 132 tx, errBegin := s.kv.BeginRo(ctx) 133 if errBegin != nil { 134 return 0, errBegin 135 } 136 id = s.txIdGen.Add(1) 137 s.txs[id] = &threadSafeTx{Tx: tx} 138 return id, nil 139 } 140 141 // renew - rollback and begin tx without changing it's `id` 142 func (s *KvServer) renew(ctx context.Context, id uint64) (err error) { 143 if s.trace { 144 s.logger.Info(fmt.Sprintf("[kv_server] renew %d %s\n", id, dbg.Stack()[:2])) 145 } 146 s.txsMapLock.Lock() 147 defer s.txsMapLock.Unlock() 148 tx, ok := s.txs[id] 149 if ok { 150 tx.Lock() 151 defer tx.Unlock() 152 tx.Rollback() 153 } 154 newTx, errBegin := s.kv.BeginRo(ctx) 155 if errBegin != nil { 156 return fmt.Errorf("kvserver: %w", err) 157 } 158 s.txs[id] = &threadSafeTx{Tx: newTx} 159 return nil 160 } 161 162 func (s *KvServer) rollback(id uint64) { 163 if s.trace { 164 s.logger.Info(fmt.Sprintf("[kv_server] rollback %d %s\n", id, dbg.Stack()[:2])) 165 } 166 s.txsMapLock.Lock() 167 defer s.txsMapLock.Unlock() 168 tx, ok := s.txs[id] 169 if ok { 170 tx.Lock() 171 defer tx.Unlock() 172 tx.Rollback() 173 delete(s.txs, id) 174 } 175 } 176 177 // with - provides exclusive access to `tx` object. Use it if you need open Cursor or run another method of `tx` object. 178 // it's ok to use same `kv.RoTx` from different goroutines, but such use must be guarded by `with` method. 179 // 180 // !Important: client may open multiple Cursors and multiple Streams on same `tx` in same time 181 // it means server must do limited amount of work inside `with` method (periodically release `tx` for other streams) 182 // long-living server-side streams must read limited-portion of data inside `with`, send this portion to 183 // client, portion of data it to client, then read next portion in another `with` call. 184 // It will allow cooperative access to `tx` object 185 func (s *KvServer) with(id uint64, f func(kv.Tx) error) error { 186 s.txsMapLock.RLock() 187 tx, ok := s.txs[id] 188 s.txsMapLock.RUnlock() 189 if !ok { 190 return fmt.Errorf("txn %d already rollback", id) 191 } 192 193 if s.trace { 194 s.logger.Info(fmt.Sprintf("[kv_server] with %d try lock %s\n", id, dbg.Stack()[:2])) 195 } 196 tx.Lock() 197 if s.trace { 198 s.logger.Info(fmt.Sprintf("[kv_server] with %d can lock %s\n", id, dbg.Stack()[:2])) 199 } 200 defer func() { 201 tx.Unlock() 202 if s.trace { 203 s.logger.Info(fmt.Sprintf("[kv_server] with %d unlock %s\n", id, dbg.Stack()[:2])) 204 } 205 }() 206 return f(tx.Tx) 207 } 208 209 func (s *KvServer) Tx(stream remote.KV_TxServer) error { 210 id, errBegin := s.begin(stream.Context()) 211 if errBegin != nil { 212 return fmt.Errorf("server-side error: %w", errBegin) 213 } 214 defer s.rollback(id) 215 216 var viewID uint64 217 if err := s.with(id, func(tx kv.Tx) error { 218 viewID = tx.ViewID() 219 return nil 220 }); err != nil { 221 return fmt.Errorf("kvserver: %w", err) 222 } 223 if err := stream.Send(&remote.Pair{ViewId: viewID, TxId: id}); err != nil { 224 return fmt.Errorf("server-side error: %w", err) 225 } 226 227 var CursorID uint32 228 type CursorInfo struct { 229 bucket string 230 c kv.Cursor 231 k, v []byte //fields to save current position of cursor - used when Tx reopen 232 } 233 cursors := map[uint32]*CursorInfo{} 234 235 txTicker := time.NewTicker(MaxTxTTL) 236 defer txTicker.Stop() 237 238 // send all items to client, if k==nil - still send it to client and break loop 239 for { 240 in, recvErr := stream.Recv() 241 if recvErr != nil { 242 if errors.Is(recvErr, io.EOF) { // termination 243 return nil 244 } 245 return fmt.Errorf("server-side error: %w", recvErr) 246 } 247 248 //TODO: protect against client - which doesn't send any requests 249 select { 250 default: 251 case <-txTicker.C: 252 for _, c := range cursors { // save positions of cursor, will restore after Tx reopening 253 k, v, err := c.c.Current() 254 if err != nil { 255 return fmt.Errorf("kvserver: %w", err) 256 } 257 c.k = bytesCopy(k) 258 c.v = bytesCopy(v) 259 } 260 261 if err := s.renew(stream.Context(), id); err != nil { 262 return err 263 } 264 if err := s.with(id, func(tx kv.Tx) error { 265 for _, c := range cursors { // restore all cursors position 266 var err error 267 c.c, err = tx.Cursor(c.bucket) 268 if err != nil { 269 return err 270 } 271 switch casted := c.c.(type) { 272 case kv.CursorDupSort: 273 v, err := casted.SeekBothRange(c.k, c.v) 274 if err != nil { 275 return fmt.Errorf("server-side error: %w", err) 276 } 277 if v == nil { // it may happen that key where we stopped disappeared after transaction reopen, then just move to next key 278 _, _, err = casted.Next() 279 if err != nil { 280 return fmt.Errorf("server-side error: %w", err) 281 } 282 } 283 case kv.Cursor: 284 if _, _, err := c.c.Seek(c.k); err != nil { 285 return fmt.Errorf("server-side error: %w", err) 286 } 287 } 288 } 289 return nil 290 }); err != nil { 291 return err 292 } 293 } 294 295 var c kv.Cursor 296 if in.BucketName == "" { 297 cInfo, ok := cursors[in.Cursor] 298 if !ok { 299 return fmt.Errorf("server-side error: unknown Cursor=%d, Op=%s", in.Cursor, in.Op) 300 } 301 c = cInfo.c 302 } 303 switch in.Op { 304 case remote.Op_OPEN: 305 CursorID++ 306 var err error 307 if err := s.with(id, func(tx kv.Tx) error { 308 c, err = tx.Cursor(in.BucketName) 309 if err != nil { 310 return err 311 } 312 return nil 313 }); err != nil { 314 return fmt.Errorf("kvserver: %w", err) 315 } 316 cursors[CursorID] = &CursorInfo{ 317 bucket: in.BucketName, 318 c: c, 319 } 320 if err := stream.Send(&remote.Pair{CursorId: CursorID}); err != nil { 321 return fmt.Errorf("kvserver: %w", err) 322 } 323 continue 324 case remote.Op_OPEN_DUP_SORT: 325 CursorID++ 326 var err error 327 if err := s.with(id, func(tx kv.Tx) error { 328 c, err = tx.CursorDupSort(in.BucketName) 329 if err != nil { 330 return err 331 } 332 return nil 333 }); err != nil { 334 return fmt.Errorf("kvserver: %w", err) 335 } 336 cursors[CursorID] = &CursorInfo{ 337 bucket: in.BucketName, 338 c: c, 339 } 340 if err := stream.Send(&remote.Pair{CursorId: CursorID}); err != nil { 341 return fmt.Errorf("server-side error: %w", err) 342 } 343 continue 344 case remote.Op_CLOSE: 345 cInfo, ok := cursors[in.Cursor] 346 if !ok { 347 return fmt.Errorf("server-side error: unknown Cursor=%d, Op=%s", in.Cursor, in.Op) 348 } 349 cInfo.c.Close() 350 delete(cursors, in.Cursor) 351 if err := stream.Send(&remote.Pair{}); err != nil { 352 return fmt.Errorf("server-side error: %w", err) 353 } 354 continue 355 default: 356 } 357 358 if err := handleOp(c, stream, in); err != nil { 359 return fmt.Errorf("server-side error: %w", err) 360 } 361 } 362 } 363 364 func handleOp(c kv.Cursor, stream remote.KV_TxServer, in *remote.Cursor) error { 365 var k, v []byte 366 var err error 367 switch in.Op { 368 case remote.Op_FIRST: 369 k, v, err = c.First() 370 case remote.Op_FIRST_DUP: 371 v, err = c.(kv.CursorDupSort).FirstDup() 372 case remote.Op_SEEK: 373 k, v, err = c.Seek(in.K) 374 case remote.Op_SEEK_BOTH: 375 v, err = c.(kv.CursorDupSort).SeekBothRange(in.K, in.V) 376 case remote.Op_CURRENT: 377 k, v, err = c.Current() 378 case remote.Op_LAST: 379 k, v, err = c.Last() 380 case remote.Op_LAST_DUP: 381 v, err = c.(kv.CursorDupSort).LastDup() 382 case remote.Op_NEXT: 383 k, v, err = c.Next() 384 case remote.Op_NEXT_DUP: 385 k, v, err = c.(kv.CursorDupSort).NextDup() 386 case remote.Op_NEXT_NO_DUP: 387 k, v, err = c.(kv.CursorDupSort).NextNoDup() 388 case remote.Op_PREV: 389 k, v, err = c.Prev() 390 //case remote.Op_PREV_DUP: 391 // k, v, err = c.(ethdb.CursorDupSort).Prev() 392 // if err != nil { 393 // return err 394 // } 395 //case remote.Op_PREV_NO_DUP: 396 // k, v, err = c.Prev() 397 // if err != nil { 398 // return err 399 // } 400 case remote.Op_SEEK_EXACT: 401 k, v, err = c.SeekExact(in.K) 402 case remote.Op_SEEK_BOTH_EXACT: 403 k, v, err = c.(kv.CursorDupSort).SeekBothExact(in.K, in.V) 404 case remote.Op_COUNT: 405 cnt, err := c.Count() 406 if err != nil { 407 return err 408 } 409 v = hexutility.EncodeTs(cnt) 410 default: 411 return fmt.Errorf("unknown operation: %s", in.Op) 412 } 413 if err != nil { 414 return err 415 } 416 417 if err := stream.Send(&remote.Pair{K: k, V: v}); err != nil { 418 return err 419 } 420 421 return nil 422 } 423 424 func bytesCopy(b []byte) []byte { 425 if b == nil { 426 return nil 427 } 428 copiedBytes := make([]byte, len(b)) 429 copy(copiedBytes, b) 430 return copiedBytes 431 } 432 433 func (s *KvServer) StateChanges(req *remote.StateChangeRequest, server remote.KV_StateChangesServer) error { 434 ch, remove := s.stateChangeStreams.Sub() 435 defer remove() 436 for { 437 select { 438 case reply := <-ch: 439 if err := server.Send(reply); err != nil { 440 return err 441 } 442 case <-s.ctx.Done(): 443 return nil 444 case <-server.Context().Done(): 445 return nil 446 } 447 } 448 } 449 450 func (s *KvServer) SendStateChanges(ctx context.Context, sc *remote.StateChangeBatch) { 451 s.stateChangeStreams.Pub(sc) 452 } 453 454 func (s *KvServer) Snapshots(ctx context.Context, _ *remote.SnapshotsRequest) (*remote.SnapshotsReply, error) { 455 if s.blockSnapshots == nil || reflect.ValueOf(s.blockSnapshots).IsNil() { // nolint 456 return &remote.SnapshotsReply{BlocksFiles: []string{}, HistoryFiles: []string{}}, nil 457 } 458 459 return &remote.SnapshotsReply{BlocksFiles: s.blockSnapshots.Files(), HistoryFiles: s.historySnapshots.Files()}, nil 460 } 461 462 type StateChangePubSub struct { 463 chans map[uint]chan *remote.StateChangeBatch 464 id uint 465 mu sync.RWMutex 466 } 467 468 func newStateChangeStreams() *StateChangePubSub { 469 return &StateChangePubSub{} 470 } 471 472 func (s *StateChangePubSub) Sub() (ch chan *remote.StateChangeBatch, remove func()) { 473 s.mu.Lock() 474 defer s.mu.Unlock() 475 if s.chans == nil { 476 s.chans = make(map[uint]chan *remote.StateChangeBatch) 477 } 478 s.id++ 479 id := s.id 480 ch = make(chan *remote.StateChangeBatch, 8) 481 s.chans[id] = ch 482 return ch, func() { s.remove(id) } 483 } 484 485 func (s *StateChangePubSub) Pub(reply *remote.StateChangeBatch) { 486 s.mu.RLock() 487 defer s.mu.RUnlock() 488 for _, ch := range s.chans { 489 common.PrioritizedSend(ch, reply) 490 } 491 } 492 493 func (s *StateChangePubSub) Len() int { 494 s.mu.RLock() 495 defer s.mu.RUnlock() 496 return len(s.chans) 497 } 498 499 func (s *StateChangePubSub) remove(id uint) { 500 s.mu.Lock() 501 defer s.mu.Unlock() 502 ch, ok := s.chans[id] 503 if !ok { // double-unsubscribe support 504 return 505 } 506 close(ch) 507 delete(s.chans, id) 508 } 509 510 // Temporal methods 511 func (s *KvServer) DomainGet(ctx context.Context, req *remote.DomainGetReq) (reply *remote.DomainGetReply, err error) { 512 reply = &remote.DomainGetReply{} 513 if err := s.with(req.TxId, func(tx kv.Tx) error { 514 ttx, ok := tx.(kv.TemporalTx) 515 if !ok { 516 return fmt.Errorf("server DB doesn't implement kv.Temporal interface") 517 } 518 if req.Latest { 519 reply.V, reply.Ok, err = ttx.DomainGet(kv.Domain(req.Table), req.K, req.K2) 520 if err != nil { 521 return err 522 } 523 } else { 524 reply.V, reply.Ok, err = ttx.DomainGetAsOf(kv.Domain(req.Table), req.K, req.K2, req.Ts) 525 if err != nil { 526 return err 527 } 528 } 529 return nil 530 }); err != nil { 531 return nil, err 532 } 533 return reply, nil 534 } 535 func (s *KvServer) HistoryGet(ctx context.Context, req *remote.HistoryGetReq) (reply *remote.HistoryGetReply, err error) { 536 reply = &remote.HistoryGetReply{} 537 if err := s.with(req.TxId, func(tx kv.Tx) error { 538 ttx, ok := tx.(kv.TemporalTx) 539 if !ok { 540 return fmt.Errorf("server DB doesn't implement kv.Temporal interface") 541 } 542 reply.V, reply.Ok, err = ttx.HistoryGet(kv.History(req.Table), req.K, req.Ts) 543 if err != nil { 544 return err 545 } 546 return nil 547 }); err != nil { 548 return nil, err 549 } 550 return reply, nil 551 } 552 553 const PageSizeLimit = 4 * 4096 554 555 func (s *KvServer) IndexRange(ctx context.Context, req *remote.IndexRangeReq) (*remote.IndexRangeReply, error) { 556 reply := &remote.IndexRangeReply{} 557 from, limit := int(req.FromTs), int(req.Limit) 558 if req.PageToken != "" { 559 var pagination remote.IndexPagination 560 if err := unmarshalPagination(req.PageToken, &pagination); err != nil { 561 return nil, err 562 } 563 from, limit = int(pagination.NextTimeStamp), int(pagination.Limit) 564 } 565 if req.PageSize <= 0 || req.PageSize > PageSizeLimit { 566 req.PageSize = PageSizeLimit 567 } 568 569 if err := s.with(req.TxId, func(tx kv.Tx) error { 570 ttx, ok := tx.(kv.TemporalTx) 571 if !ok { 572 return fmt.Errorf("server DB doesn't implement kv.Temporal interface") 573 } 574 it, err := ttx.IndexRange(kv.InvertedIdx(req.Table), req.K, from, int(req.ToTs), order.By(req.OrderAscend), limit) 575 if err != nil { 576 return err 577 } 578 for it.HasNext() { 579 v, err := it.Next() 580 if err != nil { 581 return err 582 } 583 reply.Timestamps = append(reply.Timestamps, v) 584 limit-- 585 } 586 if len(reply.Timestamps) == PageSizeLimit && it.HasNext() { 587 next, err := it.Next() 588 if err != nil { 589 return err 590 } 591 reply.NextPageToken, err = marshalPagination(&remote.IndexPagination{NextTimeStamp: int64(next), Limit: int64(limit)}) 592 if err != nil { 593 return err 594 } 595 } 596 return nil 597 }); err != nil { 598 return nil, err 599 } 600 return reply, nil 601 } 602 603 func (s *KvServer) Range(ctx context.Context, req *remote.RangeReq) (*remote.Pairs, error) { 604 from, limit := req.FromPrefix, int(req.Limit) 605 if req.PageToken != "" { 606 var pagination remote.ParisPagination 607 if err := unmarshalPagination(req.PageToken, &pagination); err != nil { 608 return nil, err 609 } 610 from, limit = pagination.NextKey, int(pagination.Limit) 611 } 612 if req.PageSize <= 0 || req.PageSize > PageSizeLimit { 613 req.PageSize = PageSizeLimit 614 } 615 616 reply := &remote.Pairs{} 617 var err error 618 if err = s.with(req.TxId, func(tx kv.Tx) error { 619 var it iter.KV 620 if req.OrderAscend { 621 it, err = tx.RangeAscend(req.Table, from, req.ToPrefix, limit) 622 if err != nil { 623 return err 624 } 625 } else { 626 it, err = tx.RangeDescend(req.Table, from, req.ToPrefix, limit) 627 if err != nil { 628 return err 629 } 630 } 631 for it.HasNext() { 632 k, v, err := it.Next() 633 if err != nil { 634 return err 635 } 636 reply.Keys = append(reply.Keys, k) 637 reply.Values = append(reply.Values, v) 638 limit-- 639 } 640 if len(reply.Keys) == PageSizeLimit && it.HasNext() { 641 nextK, _, err := it.Next() 642 if err != nil { 643 return err 644 } 645 reply.NextPageToken, err = marshalPagination(&remote.ParisPagination{NextKey: nextK, Limit: int64(limit)}) 646 if err != nil { 647 return err 648 } 649 } 650 return nil 651 }); err != nil { 652 return nil, err 653 } 654 return reply, nil 655 } 656 657 // see: https://cloud.google.com/apis/design/design_patterns 658 func marshalPagination(m proto.Message) (string, error) { 659 pageToken, err := proto.Marshal(m) 660 if err != nil { 661 return "", err 662 } 663 return base64.StdEncoding.EncodeToString(pageToken), nil 664 } 665 666 func unmarshalPagination(pageToken string, m proto.Message) error { 667 token, err := base64.StdEncoding.DecodeString(pageToken) 668 if err != nil { 669 return err 670 } 671 if err = proto.Unmarshal(token, m); err != nil { 672 return err 673 } 674 return nil 675 }