LCOV - code coverage report
Current view: top level - src/extension - bag_writer.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 290 293 99.0 %
Date: 2026-07-26 14:05:51 Functions: 46 48 95.8 %
Branches: 251 387 64.9 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (C) 2026 by Thun Lu. All rights reserved.
       3                 :            :  * Author: Thun Lu <thun.lu@zohomail.cn>
       4                 :            :  * Repo:   https://github.com/thun-res/vlink
       5                 :            :  *  _    __   __      _           __
       6                 :            :  * | |  / /  / /     (_) ____    / /__
       7                 :            :  * | | / /  / /     / / / __ \  / //_/
       8                 :            :  * | |/ /  / /___  / / / / / / / ,<
       9                 :            :  * |___/  /_____/ /_/ /_/ /_/ /_/|_|
      10                 :            :  *
      11                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
      12                 :            :  * you may not use this file except in compliance with the License.
      13                 :            :  * You may obtain a copy of the License at
      14                 :            :  *
      15                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
      16                 :            :  *
      17                 :            :  * Unless required by applicable law or agreed to in writing, software
      18                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      19                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      20                 :            :  * See the License for the specific language governing permissions and
      21                 :            :  * limitations under the License.
      22                 :            :  */
      23                 :            : 
      24                 :            : #include "./extension/bag_writer.h"
      25                 :            : 
      26                 :            : #include <algorithm>
      27                 :            : #include <atomic>
      28                 :            : #include <memory>
      29                 :            : #include <mutex>
      30                 :            : #include <shared_mutex>
      31                 :            : #include <string>
      32                 :            : #include <unordered_map>
      33                 :            : #include <vector>
      34                 :            : 
      35                 :            : #include "./base/helpers.h"
      36                 :            : #include "./base/logger.h"
      37                 :            : #include "./base/utils.h"
      38                 :            : #include "./extension/bag_plugin_interface.h"
      39                 :            : #include "./extension/schema_plugin_manager.h"
      40                 :            : #include "./extension/vcap_writer.h"
      41                 :            : #include "./extension/vdb_writer.h"
      42                 :            : 
      43                 :            : namespace vlink {
      44                 :            : 
      45                 :            : // GlobalWriter
      46                 :            : struct GlobalWriter final {
      47                 :         56 :   GlobalWriter() {
      48   [ +  -  +  - ]:         56 :     const std::string& bag_path = Utils::get_env("VLINK_BAG_PATH");
      49                 :            : 
      50         [ +  + ]:         56 :     if (!bag_path.empty()) {
      51   [ +  -  +  - ]:          4 :       VLOG_I("BagWriter: Global recorder is enabled.");
      52   [ +  -  +  - ]:          4 :       CLOG_I("BagWriter: Record path: %s.", bag_path.c_str());
      53                 :            : 
      54         [ +  - ]:          2 :       instance = BagWriter::create(bag_path);
      55                 :            : 
      56         [ +  + ]:          2 :       if VLIKELY (instance) {
      57         [ +  - ]:          1 :         instance->async_run();
      58                 :            :       } else {
      59   [ +  -  +  - ]:          2 :         CLOG_E("BagWriter: Global recorder is disabled because VLINK_BAG_PATH has an unsupported suffix.");
      60                 :            :       }
      61                 :            :     }
      62                 :         56 :   }
      63                 :            : 
      64                 :         56 :   ~GlobalWriter() {
      65         [ +  + ]:         56 :     if (instance) {
      66                 :          1 :       instance.reset();
      67                 :            :     }
      68                 :         56 :   }
      69                 :            : 
      70                 :       3892 :   static GlobalWriter& get() {
      71   [ +  +  +  -  :       3892 :     static GlobalWriter global_writer;
             +  -  -  - ]
      72                 :       3890 :     return global_writer;
      73                 :            :   }
      74                 :            : 
      75                 :            :   std::mutex mtx;
      76                 :            :   std::unordered_map<std::string, std::weak_ptr<BagWriter>> writer_map;
      77                 :            : 
      78                 :            :   std::shared_ptr<BagWriter> instance;
      79                 :            : 
      80                 :            :   VLINK_DISALLOW_COPY_AND_ASSIGN(GlobalWriter)
      81                 :            : };
      82                 :            : 
      83                 :            : // BagWriter::Impl
      84                 :            : struct BagWriter::Impl final {
      85                 :            :   std::unordered_map<int, std::string> index_to_url_map;
      86                 :            :   std::unordered_map<int, std::string> index_to_ser_map;
      87                 :            :   std::unordered_map<std::string, int> url_to_index_map;
      88                 :            :   std::unordered_map<std::string, int> ser_to_index_map;
      89                 :            :   int current_url_index{0};
      90                 :            :   int current_ser_index{0};
      91                 :            :   mutable std::shared_mutex shared_mtx;
      92                 :            : 
      93                 :            :   std::shared_ptr<BagPluginInterface> plugin_interface;
      94                 :            :   std::unordered_map<std::string, std::string> recorded_url_remap;
      95                 :            :   std::unordered_map<std::string, std::vector<std::string>> recorded_urls_by_origin;
      96                 :            :   std::unordered_map<std::string, std::string> recorded_url_origin;
      97                 :            :   mutable std::shared_mutex record_state_mtx;
      98                 :            : 
      99                 :            :   std::mutex sample_mtx;
     100                 :            :   std::unordered_map<std::string, double> url_loss_map;
     101                 :            :   std::unordered_map<std::string, double> total_url_loss_map;
     102                 :            : 
     103                 :            :   std::mutex active_write_mtx;
     104                 :            :   std::atomic<uint64_t> active_thread_id{0};
     105                 :            :   std::string active_origin_url;
     106                 :            :   int64_t active_record_result{0};
     107                 :            : 
     108                 :            :   std::atomic_bool stream_fail{false};
     109                 :            : };
     110                 :            : 
     111                 :            : // BagWriter
     112                 :        197 : std::shared_ptr<BagWriter> BagWriter::create(const std::string& path, const Config& config) {
     113         [ +  - ]:        197 :   std::string suffix_check = path;
     114                 :            : 
     115                 :        197 :   std::transform(suffix_check.begin(), suffix_check.end(), suffix_check.begin(),
     116                 :      15108 :                  [](unsigned char c) { return std::tolower(c); });
     117                 :            : 
     118   [ +  +  +  +  :        197 :   if (Helpers::has_endwith(suffix_check, ".vdb") || Helpers::has_endwith(suffix_check, ".vdbx")) {
                   +  + ]
     119         [ +  - ]:        119 :     return std::make_shared<VDBWriter>(path, config);
     120   [ +  +  +  +  :         78 :   } else if (Helpers::has_endwith(suffix_check, ".vcap") || Helpers::has_endwith(suffix_check, ".vcapx")) {
                   +  + ]
     121         [ +  - ]:         76 :     return std::make_shared<VCAPWriter>(path, config);
     122                 :            :   } else {
     123   [ +  -  +  - ]:          4 :     CLOG_E("BagWriter: Unknown bag suffix, path=%s", path.c_str());
     124                 :          2 :     return nullptr;
     125                 :            :   }
     126                 :        197 : }
     127                 :            : 
     128                 :          7 : std::shared_ptr<BagWriter> BagWriter::filter_get(const std::string& path) {
     129   [ +  +  +  -  :          7 :   static auto& instance = GlobalWriter::get();
             +  -  -  - ]
     130         [ +  - ]:          7 :   std::string suffix_check = path;
     131                 :            : 
     132                 :          7 :   std::transform(suffix_check.begin(), suffix_check.end(), suffix_check.begin(),
     133                 :        496 :                  [](unsigned char c) { return std::tolower(c); });
     134                 :            : 
     135   [ +  +  -  + ]:          7 :   const bool is_database = Helpers::has_endwith(suffix_check, ".vdb") || Helpers::has_endwith(suffix_check, ".vdbx");
     136   [ +  +  -  + ]:          7 :   const bool is_mcap = Helpers::has_endwith(suffix_check, ".vcap") || Helpers::has_endwith(suffix_check, ".vcapx");
     137                 :            : 
     138   [ +  +  +  +  :          7 :   if VUNLIKELY (!is_database && !is_mcap) {
                   +  + ]
     139   [ +  -  +  - ]:          2 :     CLOG_E("BagWriter: Unknown bag suffix, path=%s", path.c_str());
     140                 :          1 :     return nullptr;
     141                 :            :   }
     142                 :            : 
     143         [ +  - ]:          6 :   std::lock_guard lock(instance.mtx);
     144                 :            : 
     145         [ +  - ]:          6 :   auto iter = instance.writer_map.find(path);
     146                 :            : 
     147         [ +  + ]:          6 :   if (iter != instance.writer_map.end()) {
     148         [ +  - ]:          1 :     if (auto target = iter->second.lock()) {
     149                 :          1 :       return target;
     150         [ -  + ]:          1 :     }
     151                 :            : 
     152                 :            :     instance.writer_map.erase(iter);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     153                 :            :   }
     154                 :            : 
     155                 :            :   {
     156                 :          5 :     std::shared_ptr<BagWriter> target;
     157                 :            : 
     158         [ +  + ]:          5 :     if (is_mcap) {
     159   [ +  -  +  - ]:          2 :       auto* ptr = new VCAPWriter(path);
     160                 :            : 
     161   [ +  -  +  - ]:          4 :       target = std::shared_ptr<VCAPWriter>(ptr, [path](VCAPWriter* ptr) {
     162                 :            :         {
     163         [ +  - ]:          2 :           std::lock_guard lock(instance.mtx);
     164         [ +  - ]:          2 :           auto iter = instance.writer_map.find(path);
     165                 :            : 
     166   [ +  -  +  -  :          2 :           if (iter != instance.writer_map.end() && iter->second.expired()) {
                   +  - ]
     167         [ +  - ]:          2 :             instance.writer_map.erase(iter);
     168                 :            :           }
     169                 :          2 :         }
     170                 :            : 
     171         [ +  - ]:          2 :         delete ptr;
     172                 :          4 :       });
     173                 :            :     } else {
     174   [ +  -  +  - ]:          3 :       auto* ptr = new VDBWriter(path);
     175                 :            : 
     176   [ +  -  +  - ]:          6 :       target = std::shared_ptr<VDBWriter>(ptr, [path](VDBWriter* ptr) {
     177                 :            :         {
     178         [ +  - ]:          3 :           std::lock_guard lock(instance.mtx);
     179         [ +  - ]:          3 :           auto iter = instance.writer_map.find(path);
     180                 :            : 
     181   [ +  -  +  -  :          3 :           if (iter != instance.writer_map.end() && iter->second.expired()) {
                   +  - ]
     182         [ +  - ]:          3 :             instance.writer_map.erase(iter);
     183                 :            :           }
     184                 :          3 :         }
     185                 :            : 
     186         [ +  - ]:          3 :         delete ptr;
     187                 :          6 :       });
     188                 :            :     }
     189                 :            : 
     190         [ +  - ]:          5 :     target->async_run();
     191                 :            : 
     192         [ +  - ]:          5 :     instance.writer_map.emplace(path, target);
     193                 :            : 
     194                 :          5 :     return target;
     195                 :          5 :   }
     196                 :          7 : }
     197                 :            : 
     198                 :       3891 : BagWriter* BagWriter::global_get() { return GlobalWriter::get().instance.get(); }
     199                 :            : 
     200         [ +  - ]:        229 : BagWriter::BagWriter(const std::string& path, const Config& config) : impl_(std::make_unique<Impl>()) {
     201                 :            :   (void)path;
     202                 :            : 
     203         [ +  + ]:        229 :   if (!config.sync_mode) {
     204         [ +  - ]:         51 :     impl_->index_to_url_map.reserve(128);
     205         [ +  - ]:         51 :     impl_->index_to_ser_map.reserve(128);
     206         [ +  - ]:         51 :     impl_->url_to_index_map.reserve(128);
     207         [ +  - ]:         51 :     impl_->ser_to_index_map.reserve(128);
     208                 :            :   }
     209                 :            : 
     210                 :        229 :   Bytes::init_memory_pool();
     211                 :        229 : }
     212                 :            : 
     213                 :         34 : void BagWriter::get_url_meta(const std::string& url, const std::string& ser, int& url_index, int& ser_index) const {
     214                 :            :   {
     215         [ +  - ]:         34 :     std::shared_lock read_lock(impl_->shared_mtx);
     216                 :            : 
     217         [ +  - ]:         34 :     auto url_iter = impl_->url_to_index_map.find(url);
     218         [ +  - ]:         34 :     auto ser_iter = impl_->ser_to_index_map.find(ser);
     219                 :            : 
     220   [ +  +  +  +  :         34 :     if VLIKELY (url_iter != impl_->url_to_index_map.end() && ser_iter != impl_->ser_to_index_map.end()) {
                   +  + ]
     221                 :          5 :       url_index = url_iter->second;
     222                 :          5 :       ser_index = ser_iter->second;
     223                 :          5 :       return;
     224                 :            :     }
     225         [ +  + ]:         34 :   }
     226                 :            : 
     227         [ +  - ]:         29 :   std::unique_lock write_lock(impl_->shared_mtx);
     228                 :            : 
     229         [ +  - ]:         29 :   auto& url_id = impl_->url_to_index_map.try_emplace(url, -1).first->second;
     230                 :            : 
     231         [ +  + ]:         29 :   if (url_id < 0) {
     232                 :         26 :     url_id = ++impl_->current_url_index;
     233   [ +  -  +  - ]:         26 :     impl_->index_to_url_map[url_id] = url;
     234                 :            :   }
     235                 :            : 
     236         [ +  - ]:         29 :   auto& ser_id = impl_->ser_to_index_map.try_emplace(ser, -1).first->second;
     237                 :            : 
     238         [ +  + ]:         29 :   if (ser_id < 0) {
     239                 :         28 :     ser_id = ++impl_->current_ser_index;
     240   [ +  -  +  - ]:         28 :     impl_->index_to_ser_map[ser_id] = ser;
     241                 :            :   }
     242                 :            : 
     243                 :         29 :   url_index = url_id;
     244                 :         29 :   ser_index = ser_id;
     245                 :         29 : }
     246                 :            : 
     247                 :         23 : void BagWriter::get_url_meta(int url_index, int ser_index, std::string& url, std::string& ser) const {
     248         [ +  - ]:         23 :   std::shared_lock read_lock(impl_->shared_mtx);
     249                 :            : 
     250         [ +  - ]:         23 :   auto url_iter = impl_->index_to_url_map.find(url_index);
     251                 :            : 
     252         [ +  + ]:         23 :   if VLIKELY (url_iter != impl_->index_to_url_map.end()) {
     253         [ +  - ]:         22 :     url = url_iter->second;
     254                 :            :   }
     255                 :            : 
     256         [ +  - ]:         23 :   auto ser_iter = impl_->index_to_ser_map.find(ser_index);
     257                 :            : 
     258         [ +  + ]:         23 :   if VLIKELY (ser_iter != impl_->index_to_ser_map.end()) {
     259         [ +  - ]:         22 :     ser = ser_iter->second;
     260                 :            :   }
     261                 :         23 : }
     262                 :            : 
     263                 :        229 : BagWriter::~BagWriter() {
     264                 :        229 :   std::shared_ptr<BagPluginInterface> plugin_interface;
     265                 :            : 
     266                 :            :   {
     267                 :        229 :     std::shared_lock state_lock(impl_->record_state_mtx);
     268                 :        229 :     plugin_interface = impl_->plugin_interface;
     269                 :        229 :   }
     270                 :            : 
     271         [ +  + ]:        229 :   if (plugin_interface) {
     272                 :          9 :     plugin_interface->register_callback({});
     273                 :            :   }
     274                 :        229 : }
     275                 :            : 
     276                 :          2 : void BagWriter::flush_plugin() {
     277                 :          2 :   std::shared_ptr<BagPluginInterface> plugin_interface;
     278                 :            : 
     279                 :            :   {
     280         [ +  - ]:          2 :     std::shared_lock state_lock(impl_->record_state_mtx);
     281                 :          2 :     plugin_interface = impl_->plugin_interface;
     282                 :          2 :   }
     283                 :            : 
     284         [ +  - ]:          2 :   if (plugin_interface) {
     285         [ +  - ]:          2 :     plugin_interface->flush();
     286                 :            :   }
     287                 :          2 : }
     288                 :            : 
     289                 :        201 : void BagWriter::detach_plugin() {
     290                 :        201 :   std::shared_ptr<BagPluginInterface> plugin_interface;
     291                 :            : 
     292                 :            :   {
     293         [ +  - ]:        201 :     std::unique_lock state_lock(impl_->record_state_mtx);
     294                 :        201 :     plugin_interface = std::move(impl_->plugin_interface);
     295                 :        201 :   }
     296                 :            : 
     297         [ +  + ]:        201 :   if (plugin_interface) {
     298         [ +  - ]:          1 :     plugin_interface->flush();
     299                 :          1 :     plugin_interface->register_callback({});
     300                 :            :   }
     301                 :        201 : }
     302                 :            : 
     303                 :         30 : void BagWriter::bind_bag_interface(const std::shared_ptr<BagPluginInterface>& bag_interface) {
     304                 :         30 :   std::shared_ptr<BagPluginInterface> old_plugin_interface;
     305                 :            : 
     306                 :            :   {
     307         [ +  - ]:         30 :     std::shared_lock state_lock(impl_->record_state_mtx);
     308                 :         30 :     old_plugin_interface = impl_->plugin_interface;
     309                 :         30 :   }
     310                 :            : 
     311   [ +  +  +  -  :         30 :   if (old_plugin_interface && old_plugin_interface != bag_interface) {
                   +  + ]
     312         [ +  - ]:         10 :     old_plugin_interface->flush();
     313                 :         10 :     old_plugin_interface->register_callback({});
     314                 :            :   }
     315                 :            : 
     316         [ +  + ]:         30 :   if VLIKELY (bag_interface) {
     317                 :         20 :     bag_interface->bind_direction(BagPluginInterface::Direction::kWrite);
     318                 :            : 
     319         [ +  - ]:         20 :     bag_interface->register_callback([this](const Frame& frame) {
     320                 :         98 :       const bool active = impl_->active_thread_id.load(std::memory_order_acquire) == Utils::get_native_thread_id();
     321                 :            : 
     322         [ +  + ]:         49 :       if VUNLIKELY (frame.url.empty()) {
     323         [ +  - ]:          1 :         if (active) {
     324                 :          1 :           impl_->active_record_result = -1;
     325                 :            :         } else {
     326                 :          0 :           set_fail();
     327                 :            :         }
     328                 :            : 
     329                 :          1 :         return;
     330                 :            :       }
     331                 :            : 
     332   [ +  +  +  +  :         48 :       if (active && impl_->active_origin_url != frame.url) {
                   +  + ]
     333                 :          6 :         learn_recorded_url(impl_->active_origin_url, frame.url);
     334                 :            :       }
     335                 :            : 
     336                 :         48 :       const int64_t record_result = record(frame, frame.timestamp);
     337                 :            : 
     338   [ +  +  +  +  :         48 :       if VUNLIKELY (active && record_result < 0) {
                   +  + ]
     339                 :          1 :         impl_->active_record_result = record_result;
     340         [ +  + ]:         47 :       } else if VUNLIKELY (record_result < 0) {
     341                 :          2 :         set_fail();
     342                 :            :       }
     343                 :            :     });
     344                 :            :   }
     345                 :            : 
     346         [ +  - ]:         30 :   std::unique_lock state_lock(impl_->record_state_mtx);
     347                 :            : 
     348                 :         30 :   impl_->plugin_interface = bag_interface;
     349                 :         30 : }
     350                 :            : 
     351         [ +  - ]:          7 : void BagWriter::clear_bag_interface() { bind_bag_interface(nullptr); }
     352                 :            : 
     353                 :        537 : int64_t BagWriter::push(const Frame& frame) {
     354         [ +  + ]:        537 :   if VUNLIKELY (frame.url.empty()) {
     355                 :          1 :     return -1;
     356                 :            :   }
     357                 :            : 
     358   [ +  +  +  - ]:        536 :   const int64_t target_timestamp = frame.timestamp < 0 ? get_record_timestamp() : frame.timestamp;
     359                 :            : 
     360                 :        536 :   std::shared_ptr<BagPluginInterface> plugin_interface;
     361                 :            : 
     362                 :            :   {
     363         [ +  - ]:        536 :     std::shared_lock state_lock(impl_->record_state_mtx);
     364                 :        536 :     plugin_interface = impl_->plugin_interface;
     365                 :        536 :   }
     366                 :            : 
     367         [ +  + ]:        536 :   if VLIKELY (!plugin_interface) {
     368         [ +  - ]:        486 :     return record(frame, target_timestamp);
     369                 :            :   }
     370                 :            : 
     371                 :         50 :   Frame stamped;
     372                 :         50 :   const Frame* effective = &frame;
     373                 :            : 
     374         [ +  + ]:         50 :   if (frame.timestamp != target_timestamp) {
     375                 :          1 :     stamped.timestamp = target_timestamp;
     376         [ +  - ]:          1 :     stamped.url = frame.url;
     377         [ +  - ]:          1 :     stamped.ser_type = frame.ser_type;
     378                 :          1 :     stamped.schema_type = frame.schema_type;
     379                 :          1 :     stamped.action_type = frame.action_type;
     380                 :          1 :     stamped.data = Bytes::shallow_copy(frame.data.data(), frame.data.size());
     381                 :          1 :     effective = &stamped;
     382                 :            :   }
     383                 :            : 
     384         [ +  - ]:         50 :   std::lock_guard active_lock(impl_->active_write_mtx);
     385                 :            : 
     386         [ +  - ]:         50 :   impl_->active_origin_url = effective->url;
     387                 :         50 :   impl_->active_record_result = target_timestamp;
     388                 :         50 :   impl_->active_thread_id.store(Utils::get_native_thread_id(), std::memory_order_release);
     389         [ +  - ]:         50 :   plugin_interface->on_write(*effective);
     390                 :            : 
     391                 :         50 :   impl_->active_thread_id.store(0, std::memory_order_release);
     392         [ +  + ]:         50 :   const int64_t result = impl_->active_record_result < 0 ? impl_->active_record_result : target_timestamp;
     393                 :         50 :   impl_->active_origin_url.clear();
     394                 :         50 :   impl_->active_record_result = 0;
     395                 :            : 
     396                 :         50 :   return result;
     397                 :        536 : }
     398                 :            : 
     399                 :          5 : BagWriter& BagWriter::operator<<(const Frame& frame) {
     400         [ +  + ]:          5 :   if VUNLIKELY (push(frame) < 0) {
     401                 :          1 :     set_fail();
     402                 :            :   }
     403                 :            : 
     404                 :          5 :   return *this;
     405                 :            : }
     406                 :            : 
     407                 :          1 : BagWriter& BagWriter::operator<<(const SchemaData& schema_data) {
     408         [ -  + ]:          1 :   if VUNLIKELY (!push_schema(schema_data)) {
     409                 :            :     set_fail();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     410                 :            :   }
     411                 :            : 
     412                 :          1 :   return *this;
     413                 :            : }
     414                 :            : 
     415                 :         37 : bool BagWriter::fail() const noexcept { return impl_->stream_fail.load(std::memory_order_acquire); }
     416                 :            : 
     417                 :          3 : BagWriter::operator bool() const noexcept { return !impl_->stream_fail.load(std::memory_order_acquire); }
     418                 :            : 
     419                 :          1 : void BagWriter::clear() noexcept { impl_->stream_fail.store(false, std::memory_order_release); }
     420                 :            : 
     421                 :          0 : void BagWriter::close() {}
     422                 :            : 
     423                 :         28 : bool BagWriter::post_persistent_task(Callback&& callback) {
     424                 :         28 :   return post_untracked_task(std::move(callback), TaskOverflowPolicy::kReject, TaskDropPolicy::kProtected);
     425                 :            : }
     426                 :            : 
     427                 :          5 : void BagWriter::set_fail() noexcept { impl_->stream_fail.store(true, std::memory_order_release); }
     428                 :            : 
     429                 :          6 : void BagWriter::learn_recorded_url(const std::string& origin_url, const std::string& recorded_url) {
     430                 :            :   {
     431         [ +  - ]:          6 :     std::shared_lock state_lock(impl_->record_state_mtx);
     432                 :            : 
     433         [ +  - ]:          6 :     auto iter = impl_->recorded_url_remap.find(origin_url);
     434                 :            : 
     435   [ +  +  -  +  :          6 :     if (iter != impl_->recorded_url_remap.end() && iter->second == recorded_url) {
                   -  + ]
     436                 :          0 :       return;
     437                 :            :     }
     438         [ +  - ]:          6 :   }
     439                 :            : 
     440         [ +  - ]:          6 :   std::unique_lock state_lock(impl_->record_state_mtx);
     441                 :            : 
     442         [ +  - ]:          6 :   impl_->recorded_url_remap.try_emplace(origin_url, recorded_url);
     443         [ +  - ]:          6 :   auto& recorded_urls = impl_->recorded_urls_by_origin[origin_url];
     444                 :            : 
     445   [ +  -  +  - ]:          6 :   if (std::find(recorded_urls.begin(), recorded_urls.end(), recorded_url) == recorded_urls.end()) {
     446         [ +  - ]:          6 :     recorded_urls.emplace_back(recorded_url);
     447                 :            :   }
     448                 :            : 
     449   [ +  -  +  - ]:          6 :   impl_->recorded_url_origin[recorded_url] = origin_url;
     450                 :          6 : }
     451                 :            : 
     452                 :          3 : std::string BagWriter::convert_recorded_url(const std::string& url) const {
     453         [ +  - ]:          3 :   std::shared_lock state_lock(impl_->record_state_mtx);
     454                 :            : 
     455         [ +  - ]:          3 :   auto iter = impl_->recorded_url_remap.find(url);
     456                 :            : 
     457   [ +  +  +  - ]:          6 :   return iter == impl_->recorded_url_remap.end() ? url : iter->second;
     458                 :          3 : }
     459                 :            : 
     460                 :         40 : std::vector<std::string> BagWriter::recorded_urls_for_origin(const std::string& url) const {
     461         [ +  - ]:         40 :   std::shared_lock state_lock(impl_->record_state_mtx);
     462                 :         40 :   std::vector<std::string> urls;
     463         [ +  - ]:         40 :   urls.emplace_back(url);
     464                 :            : 
     465         [ +  - ]:         40 :   auto iter = impl_->recorded_urls_by_origin.find(url);
     466                 :            : 
     467         [ +  + ]:         40 :   if (iter != impl_->recorded_urls_by_origin.end()) {
     468         [ +  + ]:          3 :     for (const auto& recorded_url : iter->second) {
     469         [ +  - ]:          2 :       if (recorded_url != url) {
     470         [ +  - ]:          2 :         urls.emplace_back(recorded_url);
     471                 :            :       }
     472                 :            :     }
     473                 :            :   }
     474                 :            : 
     475                 :         80 :   return urls;
     476                 :         40 : }
     477                 :            : 
     478                 :        258 : std::string BagWriter::recover_recorded_url(const std::string& url) const {
     479         [ +  - ]:        258 :   std::shared_lock state_lock(impl_->record_state_mtx);
     480                 :            : 
     481         [ +  - ]:        258 :   auto iter = impl_->recorded_url_origin.find(url);
     482                 :            : 
     483   [ +  +  +  - ]:        516 :   return iter == impl_->recorded_url_origin.end() ? url : iter->second;
     484                 :        258 : }
     485                 :            : 
     486                 :        436 : std::mutex& BagWriter::sample_mutex() { return impl_->sample_mtx; }
     487                 :            : 
     488                 :        874 : std::unordered_map<std::string, double>& BagWriter::url_loss_map_ref() { return impl_->url_loss_map; }
     489                 :            : 
     490                 :        325 : std::unordered_map<std::string, double>& BagWriter::total_url_loss_map_ref() { return impl_->total_url_loss_map; }
     491                 :            : 
     492                 :         51 : void BagWriter::set_url_loss(const std::string& url, double loss) {
     493         [ +  + ]:         51 :   if (loss > 1) {
     494                 :          2 :     loss = -1;
     495                 :            :   }
     496                 :            : 
     497         [ +  - ]:         51 :   std::lock_guard lock(impl_->sample_mtx);
     498                 :            : 
     499         [ +  - ]:         51 :   impl_->url_loss_map[url] = loss;
     500         [ +  - ]:         51 :   impl_->total_url_loss_map[url] = loss;
     501                 :         51 : }
     502                 :            : 
     503                 :         36 : const std::string& BagWriter::get_default_tag_name() {
     504   [ +  +  +  -  :         36 :   static std::string tag_name_env_str = Utils::get_env("VLINK_BAG_TAG", "Empty");
          +  -  +  -  -  
                      - ]
     505                 :         36 :   return tag_name_env_str;
     506                 :            : }
     507                 :            : 
     508                 :        201 : const std::string& BagWriter::get_default_app_name() {
     509   [ +  +  +  - ]:        201 :   static std::string app_name = Utils::get_app_name();
     510                 :        201 :   return app_name;
     511                 :            : }
     512                 :            : 
     513   [ +  -  +  -  :        200 : SchemaPluginInterface* BagWriter::get_schema_interface() { return SchemaPluginManager::get().get_interface().get(); }
                   +  - ]
     514                 :            : 
     515                 :        201 : int32_t BagWriter::get_default_timezone_diff() { return Utils::get_timezone_diff(); }
     516                 :            : 
     517                 :        827 : std::string_view BagWriter::convert_action(ActionType type) {
     518   [ +  +  +  +  :        827 :   switch (type) {
             +  +  +  +  
                      + ]
     519                 :         63 :     case ActionType::kClientRequest:
     520                 :         63 :       return "C/Req";
     521                 :          9 :     case ActionType::kClientResponse:
     522                 :          9 :       return "C/Resp";
     523                 :          5 :     case ActionType::kServerRequest:
     524                 :          5 :       return "S/Req";
     525                 :          5 :     case ActionType::kServerResponse:
     526                 :          5 :       return "S/Resp";
     527                 :        565 :     case ActionType::kPublish:
     528                 :        565 :       return "Pub";
     529                 :        103 :     case ActionType::kSubscribe:
     530                 :        103 :       return "Sub";
     531                 :         67 :     case ActionType::kSet:
     532                 :         67 :       return "Set";
     533                 :          5 :     case ActionType::kGet:
     534                 :          5 :       return "Get";
     535                 :          5 :     default:
     536                 :          5 :       return "Unknown";
     537                 :            :   }
     538                 :            : }
     539                 :            : 
     540                 :        462 : std::string BagWriter::get_format_date(SystemClock* current, bool file_format) {
     541                 :        462 :   SystemClock time_point;
     542                 :            : 
     543         [ +  + ]:        462 :   if (current) {
     544                 :        451 :     time_point = *current;
     545                 :            :   } else {
     546         [ +  - ]:         11 :     time_point = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
     547                 :            :   }
     548                 :            : 
     549                 :        462 :   auto milliseconds = time_point.time_since_epoch().count() % 1000U;
     550                 :            : 
     551         [ +  - ]:        462 :   std::time_t now_time_t = std::chrono::system_clock::to_time_t(time_point);
     552                 :            : 
     553                 :        462 :   std::tm now_tm{};
     554                 :            : 
     555                 :            : #ifdef _WIN32
     556                 :            :   localtime_s(&now_tm, &now_time_t);
     557                 :            : #else
     558                 :        462 :   localtime_r(&now_time_t, &now_tm);
     559                 :            : #endif
     560                 :            : 
     561                 :            :   char buffer[32];
     562                 :            :   char full_buffer[64];
     563                 :            : 
     564         [ +  + ]:        462 :   if (file_format) {
     565                 :         23 :     std::strftime(buffer, sizeof(buffer), "%Y-%m-%d_%H-%M-%S", &now_tm);
     566                 :         23 :     std::snprintf(full_buffer, sizeof(full_buffer), "%s-%03lld", buffer,
     567                 :            :                   static_cast<long long>(milliseconds));  // NOLINT(runtime/int, google-runtime-int)
     568                 :            :   } else {
     569                 :        439 :     std::strftime(buffer, sizeof(buffer), "%Y/%m/%d %H:%M:%S", &now_tm);
     570                 :        439 :     std::snprintf(full_buffer, sizeof(full_buffer), "%s:%03lld", buffer,
     571                 :            :                   static_cast<long long>(milliseconds));  // NOLINT(runtime/int, google-runtime-int)
     572                 :            :   }
     573                 :            : 
     574         [ +  - ]:        462 :   return full_buffer;
     575                 :            : }
     576                 :            : 
     577                 :            : }  // namespace vlink

Generated by: LCOV version 1.14