| 690 | } |
| 691 | |
| 692 | leveldb::Status SpanDB::Index(std::shared_ptr<const Span> span, std::string* value_buf) { |
| 693 | leveldb::WriteOptions options; |
| 694 | options.sync = false; |
| 695 | |
| 696 | leveldb::Status st; |
| 697 | |
| 698 | // NOTE: Writing into time_db before id_db so that if the second write |
| 699 | // fails, the entry in time_db will be finally removed when it's out |
| 700 | // of time window. |
| 701 | |
| 702 | const int64_t start_time = span->GetStartRealTimeUs(); |
| 703 | const int64_t latency_us = span->GetEndRealTimeUs() - start_time; |
| 704 | // if latency_us < FLAGS_rpcz_save_span_min_latency_us, don't save this span |
| 705 | if (latency_us < FLAGS_rpcz_save_span_min_latency_us) { |
| 706 | return leveldb::Status::OK(); |
| 707 | } |
| 708 | BriefSpan brief; |
| 709 | brief.set_trace_id(span->trace_id()); |
| 710 | brief.set_span_id(span->span_id()); |
| 711 | brief.set_log_id(span->log_id()); |
| 712 | brief.set_type(span->type()); |
| 713 | brief.set_error_code(span->error_code()); |
| 714 | brief.set_request_size(span->request_size()); |
| 715 | brief.set_response_size(span->response_size()); |
| 716 | brief.set_start_real_us(start_time); |
| 717 | brief.set_latency_us(latency_us); |
| 718 | brief.set_full_method_name(span->full_method_name()); |
| 719 | if (!brief.SerializeToString(value_buf)) { |
| 720 | return leveldb::Status::InvalidArgument( |
| 721 | leveldb::Slice("Fail to serialize BriefSpan")); |
| 722 | } |
| 723 | // We need to make the time monotonic otherwise if older entries are |
| 724 | // overwritten by newer ones, entries in id_db associated with the older |
| 725 | // entries are not evicted. Surely we can call DB::Get() before Put(), but |
| 726 | // that would be too slow due to the storage model of leveldb. One feasible |
| 727 | // method is to maintain recent window of keys to time_db, when there's a |
| 728 | // conflict before Put(), try key+1us until an unused time is found. The |
| 729 | // window could be 5~10s. However this method needs a std::map(slow) or |
| 730 | // hashmap+queue(more memory: remember that we're just a framework), and |
| 731 | // this method can't guarantee no duplication when real time goes back |
| 732 | // significantly. |
| 733 | // Since the time to this method is ALMOST in ascending order, we use a |
| 734 | // very simple strategy: if the time is not greater than last-time, set |
| 735 | // it to be last-time + 1us. This works when time goes back because the |
| 736 | // real time is at least 1000000 / FLAGS_rpcz_max_span_per_second times faster |
| 737 | // and it will finally catch up with our time key. (provided the flag |
| 738 | // is less than 1000000). |
| 739 | int64_t time_key = start_time; |
| 740 | if (time_key <= g_last_time_key) { |
| 741 | time_key = g_last_time_key + 1; |
| 742 | } |
| 743 | g_last_time_key = time_key; |
| 744 | uint32_t time_data[2]; |
| 745 | ToBigEndian(time_key, time_data); |
| 746 | st = time_db->Put(options, |
| 747 | leveldb::Slice((char*)time_data, sizeof(time_data)), |
| 748 | leveldb::Slice(value_buf->data(), value_buf->size())); |
| 749 | if (!st.ok()) { |
no test coverage detected