LCOV - code coverage report
Current view: top level - src/extension - bag_writer.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 287 289 99.3 %
Date: 2026-06-27 19:56:04 Functions: 43 45 95.6 %
Branches: 249 387 64.3 %

           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/schema_plugin_manager.h"
      39                 :            : #include "./extension/vcap_writer.h"
      40                 :            : #include "./extension/vdb_writer.h"
      41                 :            : 
      42                 :            : namespace vlink {
      43                 :            : 
      44                 :            : // GlobalWriter
      45                 :            : struct GlobalWriter final {
      46                 :         55 :   GlobalWriter() {
      47   [ +  -  +  - ]:         55 :     const std::string& bag_path = Utils::get_env("VLINK_BAG_PATH");
      48                 :            : 
      49         [ +  + ]:         55 :     if (!bag_path.empty()) {
      50   [ +  -  +  - ]:          4 :       VLOG_I("BagWriter: Global recorder is enabled.");
      51   [ +  -  +  - ]:          4 :       CLOG_I("BagWriter: Record path: %s.", bag_path.c_str());
      52                 :            : 
      53         [ +  - ]:          2 :       instance = BagWriter::create(bag_path);
      54                 :            : 
      55         [ +  + ]:          2 :       if VLIKELY (instance) {
      56         [ +  - ]:          1 :         instance->async_run();
      57                 :            :       } else {
      58   [ +  -  +  - ]:          2 :         CLOG_E("BagWriter: Global recorder is disabled because VLINK_BAG_PATH has an unsupported suffix.");
      59                 :            :       }
      60                 :            :     }
      61                 :         55 :   }
      62                 :            : 
      63                 :         55 :   ~GlobalWriter() {
      64         [ +  + ]:         55 :     if (instance) {
      65                 :          1 :       instance.reset();
      66                 :            :     }
      67                 :         55 :   }
      68                 :            : 
      69                 :       3295 :   static GlobalWriter& get() {
      70   [ +  +  +  -  :       3295 :     static GlobalWriter global_writer;
             +  -  -  - ]
      71                 :       3292 :     return global_writer;
      72                 :            :   }
      73                 :            : 
      74                 :            :   std::mutex mtx;
      75                 :            :   std::unordered_map<std::string, std::weak_ptr<BagWriter>> writer_map;
      76                 :            : 
      77                 :            :   std::shared_ptr<BagWriter> instance;
      78                 :            : 
      79                 :            :   VLINK_DISALLOW_COPY_AND_ASSIGN(GlobalWriter)
      80                 :            : };
      81                 :            : 
      82                 :            : // BagWriter::Impl
      83                 :            : struct BagWriter::Impl final {
      84                 :            :   std::unordered_map<int, std::string> index_to_url_map;
      85                 :            :   std::unordered_map<int, std::string> index_to_ser_map;
      86                 :            :   std::unordered_map<std::string, int> url_to_index_map;
      87                 :            :   std::unordered_map<std::string, int> ser_to_index_map;
      88                 :            :   int current_url_index{0};
      89                 :            :   int current_ser_index{0};
      90                 :            :   mutable std::shared_mutex shared_mtx;
      91                 :            : 
      92                 :            :   std::shared_ptr<BagPluginInterface> plugin_interface;
      93                 :            :   std::unordered_map<std::string, std::string> recorded_url_remap;
      94                 :            :   std::unordered_map<std::string, std::vector<std::string>> recorded_urls_by_origin;
      95                 :            :   std::unordered_map<std::string, std::string> recorded_url_origin;
      96                 :            :   mutable std::shared_mutex record_state_mtx;
      97                 :            : 
      98                 :            :   std::mutex sample_mtx;
      99                 :            :   std::unordered_map<std::string, double> url_loss_map;
     100                 :            :   std::unordered_map<std::string, double> total_url_loss_map;
     101                 :            : 
     102                 :            :   std::mutex active_write_mtx;
     103                 :            :   std::atomic<uint64_t> active_thread_id{0};
     104                 :            :   std::string active_origin_url;
     105                 :            :   bool active_immediate{false};
     106                 :            :   int64_t active_record_result{0};
     107                 :            : 
     108                 :            :   std::atomic_bool stream_fail{false};
     109                 :            : };
     110                 :            : 
     111                 :            : // BagWriter
     112                 :        152 : std::shared_ptr<BagWriter> BagWriter::create(const std::string& path, const Config& config) {
     113         [ +  - ]:        152 :   std::string suffix_check = path;
     114                 :            : 
     115                 :        152 :   std::transform(suffix_check.begin(), suffix_check.end(), suffix_check.begin(),
     116                 :      12262 :                  [](unsigned char c) { return std::tolower(c); });
     117                 :            : 
     118   [ +  +  +  +  :        152 :   if (Helpers::has_endwith(suffix_check, ".vdb") || Helpers::has_endwith(suffix_check, ".vdbx")) {
                   +  + ]
     119         [ +  - ]:         88 :     return std::make_shared<VDBWriter>(path, config);
     120   [ +  +  +  +  :         64 :   } else if (Helpers::has_endwith(suffix_check, ".vcap") || Helpers::has_endwith(suffix_check, ".vcapx")) {
                   +  + ]
     121         [ +  - ]:         62 :     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                 :        152 : }
     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                 :        502 :                  [](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                 :       3294 : BagWriter* BagWriter::global_get() { return GlobalWriter::get().instance.get(); }
     199                 :            : 
     200         [ +  - ]:        182 : BagWriter::BagWriter(const std::string& path, const Config& config) : impl_(std::make_unique<Impl>()) {
     201                 :            :   (void)path;
     202                 :            :   (void)config;
     203                 :            : 
     204         [ +  + ]:        182 :   if (!config.sync_mode) {
     205         [ +  - ]:         43 :     impl_->index_to_url_map.reserve(128);
     206         [ +  - ]:         43 :     impl_->index_to_ser_map.reserve(128);
     207         [ +  - ]:         43 :     impl_->url_to_index_map.reserve(128);
     208         [ +  - ]:         43 :     impl_->ser_to_index_map.reserve(128);
     209                 :            :   }
     210                 :            : 
     211                 :        182 :   Bytes::init_memory_pool();
     212                 :        182 : }
     213                 :            : 
     214                 :         22 : void BagWriter::get_url_meta(const std::string& url, const std::string& ser, int& url_index, int& ser_index) const {
     215                 :            :   {
     216         [ +  - ]:         22 :     std::shared_lock read_lock(impl_->shared_mtx);
     217                 :            : 
     218         [ +  - ]:         22 :     auto url_iter = impl_->url_to_index_map.find(url);
     219         [ +  - ]:         22 :     auto ser_iter = impl_->ser_to_index_map.find(ser);
     220                 :            : 
     221   [ +  +  +  +  :         22 :     if VLIKELY (url_iter != impl_->url_to_index_map.end() && ser_iter != impl_->ser_to_index_map.end()) {
                   +  + ]
     222                 :          1 :       url_index = url_iter->second;
     223                 :          1 :       ser_index = ser_iter->second;
     224                 :          1 :       return;
     225                 :            :     }
     226         [ +  + ]:         22 :   }
     227                 :            : 
     228         [ +  - ]:         21 :   std::unique_lock write_lock(impl_->shared_mtx);
     229                 :            : 
     230         [ +  - ]:         21 :   auto& url_id = impl_->url_to_index_map.try_emplace(url, -1).first->second;
     231                 :            : 
     232         [ +  + ]:         21 :   if (url_id < 0) {
     233                 :         20 :     url_id = ++impl_->current_url_index;
     234   [ +  -  +  - ]:         20 :     impl_->index_to_url_map[url_id] = url;
     235                 :            :   }
     236                 :            : 
     237         [ +  - ]:         21 :   auto& ser_id = impl_->ser_to_index_map.try_emplace(ser, -1).first->second;
     238                 :            : 
     239         [ +  + ]:         21 :   if (ser_id < 0) {
     240                 :         20 :     ser_id = ++impl_->current_ser_index;
     241   [ +  -  +  - ]:         20 :     impl_->index_to_ser_map[ser_id] = ser;
     242                 :            :   }
     243                 :            : 
     244                 :         21 :   url_index = url_id;
     245                 :         21 :   ser_index = ser_id;
     246                 :         21 : }
     247                 :            : 
     248                 :         14 : void BagWriter::get_url_meta(int url_index, int ser_index, std::string& url, std::string& ser) const {
     249         [ +  - ]:         14 :   std::shared_lock read_lock(impl_->shared_mtx);
     250                 :            : 
     251         [ +  - ]:         14 :   auto url_iter = impl_->index_to_url_map.find(url_index);
     252                 :            : 
     253         [ +  + ]:         14 :   if (url_iter != impl_->index_to_url_map.end()) {
     254         [ +  - ]:         13 :     url = url_iter->second;
     255                 :            :   }
     256                 :            : 
     257         [ +  - ]:         14 :   auto ser_iter = impl_->index_to_ser_map.find(ser_index);
     258                 :            : 
     259         [ +  + ]:         14 :   if (ser_iter != impl_->index_to_ser_map.end()) {
     260         [ +  - ]:         13 :     ser = ser_iter->second;
     261                 :            :   }
     262                 :         14 : }
     263                 :            : 
     264                 :        182 : BagWriter::~BagWriter() {
     265                 :        182 :   std::shared_ptr<BagPluginInterface> plugin_interface;
     266                 :            : 
     267                 :            :   {
     268                 :        182 :     std::shared_lock state_lock(impl_->record_state_mtx);
     269                 :        182 :     plugin_interface = impl_->plugin_interface;
     270                 :        182 :   }
     271                 :            : 
     272         [ +  + ]:        182 :   if (plugin_interface) {
     273                 :          9 :     plugin_interface->register_callback({});
     274                 :            :   }
     275                 :        182 : }
     276                 :            : 
     277                 :          2 : void BagWriter::flush_plugin() {
     278                 :          2 :   std::shared_ptr<BagPluginInterface> plugin_interface;
     279                 :            : 
     280                 :            :   {
     281         [ +  - ]:          2 :     std::shared_lock state_lock(impl_->record_state_mtx);
     282                 :          2 :     plugin_interface = impl_->plugin_interface;
     283                 :          2 :   }
     284                 :            : 
     285         [ +  - ]:          2 :   if (plugin_interface) {
     286         [ +  - ]:          2 :     plugin_interface->flush();
     287                 :            :   }
     288                 :          2 : }
     289                 :            : 
     290                 :        156 : void BagWriter::detach_plugin() {
     291                 :        156 :   std::shared_ptr<BagPluginInterface> plugin_interface;
     292                 :            : 
     293                 :            :   {
     294         [ +  - ]:        156 :     std::unique_lock state_lock(impl_->record_state_mtx);
     295                 :        156 :     plugin_interface = std::move(impl_->plugin_interface);
     296                 :        156 :   }
     297                 :            : 
     298         [ +  + ]:        156 :   if (plugin_interface) {
     299         [ +  - ]:          1 :     plugin_interface->flush();
     300                 :          1 :     plugin_interface->register_callback({});
     301                 :            :   }
     302                 :        156 : }
     303                 :            : 
     304                 :         16 : void BagWriter::bind_plugin_interface(const std::shared_ptr<BagPluginInterface>& plugin_interface) {
     305                 :         16 :   std::shared_ptr<BagPluginInterface> old_plugin_interface;
     306                 :            : 
     307                 :            :   {
     308         [ +  - ]:         16 :     std::shared_lock state_lock(impl_->record_state_mtx);
     309                 :         16 :     old_plugin_interface = impl_->plugin_interface;
     310                 :         16 :   }
     311                 :            : 
     312   [ +  +  +  -  :         16 :   if (old_plugin_interface && old_plugin_interface != plugin_interface) {
                   +  + ]
     313         [ +  - ]:          3 :     old_plugin_interface->flush();
     314                 :          3 :     old_plugin_interface->register_callback({});
     315                 :            :   }
     316                 :            : 
     317         [ +  + ]:         16 :   if VLIKELY (plugin_interface) {
     318                 :         13 :     plugin_interface->bind_direction(BagPluginInterface::Direction::kWrite);
     319                 :            : 
     320         [ +  - ]:         13 :     plugin_interface->register_callback([this](const Frame& frame) {
     321                 :         36 :       const bool active = impl_->active_thread_id.load(std::memory_order_acquire) == Utils::get_native_thread_id();
     322                 :            : 
     323         [ +  + ]:         18 :       if VUNLIKELY (frame.url.empty()) {
     324         [ +  - ]:          1 :         if (active) {
     325                 :          1 :           impl_->active_record_result = -1;
     326                 :            :         }
     327                 :            : 
     328                 :          1 :         return;
     329                 :            :       }
     330                 :            : 
     331   [ +  +  +  +  :         17 :       if (active && impl_->active_origin_url != frame.url) {
                   +  + ]
     332                 :          6 :         learn_recorded_url(impl_->active_origin_url, frame.url);
     333                 :            :       }
     334                 :            : 
     335   [ +  +  -  + ]:         17 :       const bool immediate = active && impl_->active_immediate;
     336                 :            : 
     337                 :         17 :       const int64_t record_result = record(frame, immediate);
     338                 :            : 
     339   [ +  +  +  + ]:         17 :       if (active && record_result < 0) {
     340                 :          1 :         impl_->active_record_result = record_result;
     341                 :            :       }
     342                 :            :     });
     343                 :            :   }
     344                 :            : 
     345         [ +  - ]:         16 :   std::unique_lock state_lock(impl_->record_state_mtx);
     346                 :            : 
     347                 :         16 :   impl_->plugin_interface = plugin_interface;
     348                 :         16 : }
     349                 :            : 
     350         [ #  # ]:          0 : void BagWriter::clear_plugin_interface() { bind_plugin_interface(nullptr); }
     351                 :            : 
     352                 :        372 : int64_t BagWriter::push(const Frame& frame, bool immediate) {
     353         [ +  + ]:        372 :   if VUNLIKELY (frame.url.empty()) {
     354                 :          1 :     return -1;
     355                 :            :   }
     356                 :            : 
     357   [ +  +  +  - ]:        371 :   const int64_t target_timestamp = frame.timestamp < 0 ? get_record_timestamp() : frame.timestamp;
     358                 :            : 
     359                 :        371 :   std::shared_ptr<BagPluginInterface> plugin_interface;
     360                 :            : 
     361                 :            :   {
     362         [ +  - ]:        371 :     std::shared_lock state_lock(impl_->record_state_mtx);
     363                 :        371 :     plugin_interface = impl_->plugin_interface;
     364                 :        371 :   }
     365                 :            : 
     366                 :        371 :   Frame stamped;
     367                 :        371 :   const Frame* effective = &frame;
     368                 :            : 
     369         [ +  + ]:        371 :   if (frame.timestamp != target_timestamp) {
     370                 :          4 :     stamped.timestamp = target_timestamp;
     371         [ +  - ]:          4 :     stamped.url = frame.url;
     372         [ +  - ]:          4 :     stamped.ser_type = frame.ser_type;
     373                 :          4 :     stamped.schema_type = frame.schema_type;
     374                 :          4 :     stamped.action_type = frame.action_type;
     375                 :          4 :     stamped.data = Bytes::shallow_copy(frame.data.data(), frame.data.size());
     376                 :          4 :     effective = &stamped;
     377                 :            :   }
     378                 :            : 
     379         [ +  + ]:        371 :   if VLIKELY (!plugin_interface) {
     380         [ +  - ]:        352 :     return record(*effective, immediate);
     381                 :            :   }
     382                 :            : 
     383         [ +  - ]:         19 :   std::lock_guard active_lock(impl_->active_write_mtx);
     384                 :            : 
     385         [ +  - ]:         19 :   impl_->active_origin_url = effective->url;
     386                 :         19 :   impl_->active_immediate = immediate;
     387                 :         19 :   impl_->active_record_result = target_timestamp;
     388                 :         19 :   impl_->active_thread_id.store(Utils::get_native_thread_id(), std::memory_order_release);
     389         [ +  - ]:         19 :   plugin_interface->on_write(*effective);
     390                 :            : 
     391                 :         19 :   impl_->active_thread_id.store(0, std::memory_order_release);
     392         [ +  + ]:         19 :   const int64_t result = impl_->active_record_result < 0 ? impl_->active_record_result : target_timestamp;
     393                 :         19 :   impl_->active_origin_url.clear();
     394                 :         19 :   impl_->active_immediate = false;
     395                 :         19 :   impl_->active_record_result = 0;
     396                 :            : 
     397                 :         19 :   return result;
     398                 :        371 : }
     399                 :            : 
     400                 :          5 : BagWriter& BagWriter::operator<<(const Frame& frame) {
     401         [ +  + ]:          5 :   if VUNLIKELY (push(frame, false) < 0) {
     402                 :          1 :     impl_->stream_fail.store(true, std::memory_order_relaxed);
     403                 :            :   }
     404                 :            : 
     405                 :          5 :   return *this;
     406                 :            : }
     407                 :            : 
     408                 :          1 : BagWriter& BagWriter::operator<<(const SchemaData& schema_data) {
     409         [ -  + ]:          1 :   if VUNLIKELY (!push_schema(schema_data, false)) {
     410                 :            :     impl_->stream_fail.store(true, std::memory_order_relaxed);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     411                 :            :   }
     412                 :            : 
     413                 :          1 :   return *this;
     414                 :            : }
     415                 :            : 
     416                 :          4 : bool BagWriter::fail() const noexcept { return impl_->stream_fail.load(std::memory_order_relaxed); }
     417                 :            : 
     418                 :          3 : BagWriter::operator bool() const noexcept { return !impl_->stream_fail.load(std::memory_order_relaxed); }
     419                 :            : 
     420                 :          1 : void BagWriter::clear() noexcept { impl_->stream_fail.store(false, std::memory_order_relaxed); }
     421                 :            : 
     422                 :          6 : void BagWriter::learn_recorded_url(const std::string& origin_url, const std::string& recorded_url) {
     423                 :            :   {
     424         [ +  - ]:          6 :     std::shared_lock state_lock(impl_->record_state_mtx);
     425                 :            : 
     426         [ +  - ]:          6 :     auto iter = impl_->recorded_url_remap.find(origin_url);
     427                 :            : 
     428   [ +  +  -  +  :          6 :     if (iter != impl_->recorded_url_remap.end() && iter->second == recorded_url) {
                   -  + ]
     429                 :          0 :       return;
     430                 :            :     }
     431         [ +  - ]:          6 :   }
     432                 :            : 
     433         [ +  - ]:          6 :   std::unique_lock state_lock(impl_->record_state_mtx);
     434                 :            : 
     435         [ +  - ]:          6 :   impl_->recorded_url_remap.try_emplace(origin_url, recorded_url);
     436         [ +  - ]:          6 :   auto& recorded_urls = impl_->recorded_urls_by_origin[origin_url];
     437                 :            : 
     438   [ +  -  +  - ]:          6 :   if (std::find(recorded_urls.begin(), recorded_urls.end(), recorded_url) == recorded_urls.end()) {
     439         [ +  - ]:          6 :     recorded_urls.emplace_back(recorded_url);
     440                 :            :   }
     441                 :            : 
     442   [ +  -  +  - ]:          6 :   impl_->recorded_url_origin[recorded_url] = origin_url;
     443                 :          6 : }
     444                 :            : 
     445                 :          3 : std::string BagWriter::convert_recorded_url(const std::string& url) const {
     446         [ +  - ]:          3 :   std::shared_lock state_lock(impl_->record_state_mtx);
     447                 :            : 
     448         [ +  - ]:          3 :   auto iter = impl_->recorded_url_remap.find(url);
     449                 :            : 
     450   [ +  +  +  - ]:          6 :   return iter == impl_->recorded_url_remap.end() ? url : iter->second;
     451                 :          3 : }
     452                 :            : 
     453                 :         33 : std::vector<std::string> BagWriter::recorded_urls_for_origin(const std::string& url) const {
     454         [ +  - ]:         33 :   std::shared_lock state_lock(impl_->record_state_mtx);
     455                 :         33 :   std::vector<std::string> urls;
     456         [ +  - ]:         33 :   urls.emplace_back(url);
     457                 :            : 
     458         [ +  - ]:         33 :   auto iter = impl_->recorded_urls_by_origin.find(url);
     459                 :            : 
     460         [ +  + ]:         33 :   if (iter != impl_->recorded_urls_by_origin.end()) {
     461         [ +  + ]:          3 :     for (const auto& recorded_url : iter->second) {
     462         [ +  - ]:          2 :       if (recorded_url != url) {
     463         [ +  - ]:          2 :         urls.emplace_back(recorded_url);
     464                 :            :       }
     465                 :            :     }
     466                 :            :   }
     467                 :            : 
     468                 :         66 :   return urls;
     469                 :         33 : }
     470                 :            : 
     471                 :        221 : std::string BagWriter::recover_recorded_url(const std::string& url) const {
     472         [ +  - ]:        221 :   std::shared_lock state_lock(impl_->record_state_mtx);
     473                 :            : 
     474         [ +  - ]:        221 :   auto iter = impl_->recorded_url_origin.find(url);
     475                 :            : 
     476   [ +  +  +  - ]:        442 :   return iter == impl_->recorded_url_origin.end() ? url : iter->second;
     477                 :        221 : }
     478                 :            : 
     479                 :        365 : std::mutex& BagWriter::sample_mutex() { return impl_->sample_mtx; }
     480                 :            : 
     481                 :        708 : std::unordered_map<std::string, double>& BagWriter::url_loss_map_ref() { return impl_->url_loss_map; }
     482                 :            : 
     483                 :        264 : std::unordered_map<std::string, double>& BagWriter::total_url_loss_map_ref() { return impl_->total_url_loss_map; }
     484                 :            : 
     485                 :         44 : void BagWriter::set_url_loss(const std::string& url, double loss) {
     486         [ +  + ]:         44 :   if (loss > 1) {
     487                 :          2 :     loss = -1;
     488                 :            :   }
     489                 :            : 
     490         [ +  - ]:         44 :   std::lock_guard lock(impl_->sample_mtx);
     491                 :            : 
     492         [ +  - ]:         44 :   impl_->url_loss_map[url] = loss;
     493         [ +  - ]:         44 :   impl_->total_url_loss_map[url] = loss;
     494                 :         44 : }
     495                 :            : 
     496                 :          8 : const std::string& BagWriter::get_default_tag_name() {
     497   [ +  +  +  -  :          8 :   static std::string tag_name_env_str = Utils::get_env("VLINK_BAG_TAG", "Empty");
          +  -  +  -  -  
                      - ]
     498                 :          8 :   return tag_name_env_str;
     499                 :            : }
     500                 :            : 
     501                 :        156 : const std::string& BagWriter::get_default_app_name() {
     502   [ +  +  +  - ]:        156 :   static std::string app_name = Utils::get_app_name();
     503                 :        156 :   return app_name;
     504                 :            : }
     505                 :            : 
     506   [ +  -  +  -  :        155 : SchemaPluginInterface* BagWriter::get_schema_interface() { return SchemaPluginManager::get().get_interface().get(); }
                   +  - ]
     507                 :            : 
     508                 :        156 : int32_t BagWriter::get_default_timezone_diff() { return Utils::get_timezone_diff(); }
     509                 :            : 
     510                 :        620 : std::string_view BagWriter::convert_action(ActionType type) {
     511   [ +  +  +  +  :        620 :   switch (type) {
             +  +  +  +  
                      + ]
     512                 :         63 :     case ActionType::kClientRequest:
     513                 :         63 :       return "C/Req";
     514                 :          9 :     case ActionType::kClientResponse:
     515                 :          9 :       return "C/Resp";
     516                 :          5 :     case ActionType::kServerRequest:
     517                 :          5 :       return "S/Req";
     518                 :          5 :     case ActionType::kServerResponse:
     519                 :          5 :       return "S/Resp";
     520                 :        455 :     case ActionType::kPublish:
     521                 :        455 :       return "Pub";
     522                 :          5 :     case ActionType::kSubscribe:
     523                 :          5 :       return "Sub";
     524                 :         67 :     case ActionType::kSet:
     525                 :         67 :       return "Set";
     526                 :          5 :     case ActionType::kGet:
     527                 :          5 :       return "Get";
     528                 :          6 :     default:
     529                 :          6 :       return "Unknown";
     530                 :            :   }
     531                 :            : }
     532                 :            : 
     533                 :        383 : std::string BagWriter::get_format_date(SystemClock* current, bool file_format) {
     534                 :        383 :   SystemClock time_point;
     535                 :            : 
     536         [ +  + ]:        383 :   if (current) {
     537                 :        380 :     time_point = *current;
     538                 :            :   } else {
     539         [ +  - ]:          3 :     time_point = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
     540                 :            :   }
     541                 :            : 
     542                 :        383 :   auto milliseconds = time_point.time_since_epoch().count() % 1000U;
     543                 :            : 
     544         [ +  - ]:        383 :   std::time_t now_time_t = std::chrono::system_clock::to_time_t(time_point);
     545                 :            : 
     546                 :        383 :   std::tm now_tm{};
     547                 :            : 
     548                 :            : #ifdef _WIN32
     549                 :            :   localtime_s(&now_tm, &now_time_t);
     550                 :            : #else
     551                 :        383 :   localtime_r(&now_time_t, &now_tm);
     552                 :            : #endif
     553                 :            : 
     554                 :            :   char buffer[32];
     555                 :            :   char full_buffer[64];
     556                 :            : 
     557         [ +  + ]:        383 :   if (file_format) {
     558                 :         15 :     std::strftime(buffer, sizeof(buffer), "%Y-%m-%d_%H-%M-%S", &now_tm);
     559                 :         15 :     std::snprintf(full_buffer, sizeof(full_buffer), "%s-%03lld", buffer,
     560                 :            :                   static_cast<long long>(milliseconds));  // NOLINT(runtime/int, google-runtime-int)
     561                 :            :   } else {
     562                 :        368 :     std::strftime(buffer, sizeof(buffer), "%Y/%m/%d %H:%M:%S", &now_tm);
     563                 :        368 :     std::snprintf(full_buffer, sizeof(full_buffer), "%s:%03lld", buffer,
     564                 :            :                   static_cast<long long>(milliseconds));  // NOLINT(runtime/int, google-runtime-int)
     565                 :            :   }
     566                 :            : 
     567         [ +  - ]:        383 :   return full_buffer;
     568                 :            : }
     569                 :            : 
     570                 :            : }  // namespace vlink

Generated by: LCOV version 1.14