LCOV - code coverage report
Current view: top level - src/extension - vdb_writer.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 916 958 95.6 %
Date: 2026-07-26 14:05:51 Functions: 47 56 83.9 %
Branches: 1020 1776 57.4 %

           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/vdb_writer.h"
      25                 :            : 
      26                 :            : #include <algorithm>
      27                 :            : #include <array>
      28                 :            : #include <cstdint>
      29                 :            : #include <cstdio>
      30                 :            : #include <filesystem>
      31                 :            : #include <fstream>
      32                 :            : #include <memory>
      33                 :            : #include <mutex>
      34                 :            : #include <string>
      35                 :            : #include <string_view>
      36                 :            : #include <unordered_map>
      37                 :            : #include <unordered_set>
      38                 :            : #include <utility>
      39                 :            : #include <vector>
      40                 :            : 
      41                 :            : #include "./base/elapsed_timer.h"
      42                 :            : #include "./base/helpers.h"
      43                 :            : #include "./base/logger.h"
      44                 :            : #include "./version.h"
      45                 :            : 
      46                 :            : // json
      47                 :            : #include <nlohmann/json.hpp>
      48                 :            : 
      49                 :            : #ifdef VLINK_ENABLE_SQLITE
      50                 :            : #include <sqlite3.h>
      51                 :            : #endif
      52                 :            : 
      53                 :            : // schema_plugin
      54                 :            : #include "./extension/schema_plugin_interface.h"
      55                 :            : 
      56                 :            : namespace vlink {
      57                 :            : 
      58                 :       6560 : [[maybe_unused]] static constexpr int get_column(int column) noexcept { return column + 1; }
      59                 :            : 
      60                 :            : static constexpr int kSyncWriteInterval = 1000;  // ms
      61                 :            : static constexpr int kCompressMaxIgnoreCnt = 5;
      62                 :            : static constexpr double kCompressMaxRatio = 0.95;
      63                 :            : 
      64                 :            : // VDBWriter::Impl
      65                 :            : struct VDBWriter::Impl final {  // NOLINT(clang-analyzer-optin.performance.Padding)
      66                 :            :   // UrlMsgInfo
      67                 :            :   struct UrlMsgInfo final {
      68                 :            :     int index{0};
      69                 :            :     size_t count{0};
      70                 :            :     size_t size{0};
      71                 :            :     int64_t first_timestamp{-1};
      72                 :            :     int64_t last_timestamp{-1};
      73                 :            :     int64_t previous_segment_last_timestamp{-1};
      74                 :            :     double freq{0};
      75                 :            :     double loss{0};
      76                 :            :     std::string url_type;
      77                 :            :     std::string ser_type;
      78                 :            :     SchemaType schema_type{SchemaType::kUnknown};
      79                 :            : 
      80                 :            :     bool operator<(const UrlMsgInfo& target) const noexcept { return index < target.index; }
      81                 :            :   };
      82                 :            : 
      83                 :            :   struct WriteStateSnapshot final {
      84                 :            :     int64_t current_row{0};
      85                 :            :     int64_t current_size{0};
      86                 :            :     bool has_oversize{false};
      87                 :            :     int64_t last_timestamp{0};
      88                 :            :     std::vector<std::string> total_url_list;
      89                 :            :     int64_t total_current_row{0};
      90                 :            :     int64_t total_current_size{0};
      91                 :            :     int64_t total_timestamp{0};
      92                 :            :     std::unordered_map<std::string, UrlMsgInfo> url_map;
      93                 :            :     std::unordered_set<std::string> ser_map;
      94                 :            :     std::unordered_map<std::string, UrlMsgInfo> total_url_map;
      95                 :            :     std::unordered_map<std::string, SchemaData> total_schema_map;
      96                 :            :     std::unordered_map<std::string, int64_t> compress_ignore_map;
      97                 :            :     std::string write_url_type;
      98                 :            : 
      99                 :        465 :     explicit WriteStateSnapshot(const VDBWriter::Impl& impl)
     100                 :        465 :         : current_row(impl.current_row),
     101                 :        465 :           current_size(impl.current_size),
     102                 :        465 :           has_oversize(impl.has_oversize),
     103                 :        465 :           last_timestamp(impl.last_timestamp),
     104                 :        465 :           total_url_list(impl.total_url_list),
     105                 :        465 :           total_current_row(impl.total_current_row),
     106                 :        465 :           total_current_size(impl.total_current_size),
     107                 :        465 :           total_timestamp(impl.total_timestamp),
     108         [ +  - ]:        465 :           url_map(impl.url_map),
     109         [ +  - ]:        465 :           ser_map(impl.ser_map),
     110         [ +  - ]:        465 :           total_url_map(impl.total_url_map),
     111         [ +  - ]:        465 :           total_schema_map(impl.total_schema_map),
     112         [ +  - ]:        465 :           compress_ignore_map(impl.compress_ignore_map),
     113         [ +  - ]:        465 :           write_url_type(impl.write_url_type) {}
     114                 :            : 
     115                 :          2 :     void restore(VDBWriter::Impl& impl) const {
     116                 :          2 :       impl.current_row = current_row;
     117                 :          2 :       impl.current_size = current_size;
     118                 :          2 :       impl.has_oversize = has_oversize;
     119                 :          2 :       impl.last_timestamp = last_timestamp;
     120                 :          2 :       impl.total_url_list = total_url_list;
     121                 :          2 :       impl.total_current_row = total_current_row;
     122                 :          2 :       impl.total_current_size = total_current_size;
     123                 :          2 :       impl.total_timestamp = total_timestamp;
     124                 :          2 :       impl.url_map = url_map;
     125                 :          2 :       impl.ser_map = ser_map;
     126                 :          2 :       impl.total_url_map = total_url_map;
     127                 :          2 :       impl.total_schema_map = total_schema_map;
     128                 :          2 :       impl.compress_ignore_map = compress_ignore_map;
     129                 :          2 :       impl.write_url_type = write_url_type;
     130                 :          2 :     }
     131                 :            :   };
     132                 :            : 
     133                 :            :   struct MemoryCharge final {
     134                 :         13 :     MemoryCharge(std::atomic<int64_t>& counter, int64_t bytes) : value(&counter), size(bytes) {}
     135                 :            : 
     136                 :         26 :     MemoryCharge(MemoryCharge&& other) noexcept : value(std::exchange(other.value, nullptr)), size(other.size) {}
     137                 :            : 
     138                 :         39 :     ~MemoryCharge() {
     139         [ +  + ]:         39 :       if (value) {
     140                 :         13 :         value->fetch_sub(size, std::memory_order_relaxed);
     141                 :            :       }
     142                 :         39 :     }
     143                 :            : 
     144                 :            :     std::atomic<int64_t>* value;
     145                 :            :     int64_t size;
     146                 :            : 
     147                 :            :     VLINK_DISALLOW_COPY_AND_ASSIGN(MemoryCharge)
     148                 :            :   };
     149                 :            : 
     150                 :            :   std::atomic_bool is_dumping{false};
     151                 :            :   std::atomic_bool is_split_mode{false};
     152                 :            :   std::atomic<int> split_index{0};
     153                 :            :   std::atomic<int64_t> memory_size{0};
     154                 :            :   std::atomic_bool in_cached{false};
     155                 :            :   std::atomic<int64_t> cached_size{0};
     156                 :            :   std::atomic_bool quit_flag{false};
     157                 :            : 
     158                 :            :   std::string path;
     159                 :            :   std::filesystem::path split_output_dir;
     160                 :            :   std::string base_dir;
     161                 :            :   std::string base_name;
     162                 :            :   BagWriter::Config config;
     163                 :            :   ElapsedTimer elapsed_timer{ElapsedTimer::kMicro};
     164                 :            : 
     165                 :            :   int64_t current_row{0};
     166                 :            :   int64_t current_size{0};
     167                 :            :   bool has_oversize{false};
     168                 :            : 
     169                 :            :   int64_t last_timestamp{0};
     170                 :            : 
     171                 :            :   BagWriter::SystemClock time_start;
     172                 :            :   BagWriter::SystemClock time_current;
     173                 :            :   int64_t start_timestamp{0};
     174                 :            : 
     175                 :            :   std::vector<std::string> split_file_list;
     176                 :            :   bool split_before{false};
     177                 :            :   bool split_first{false};
     178                 :            : 
     179                 :            :   std::vector<std::string> total_url_list;
     180                 :            :   int64_t total_current_row{0};
     181                 :            :   int64_t total_current_size{0};
     182                 :            :   int64_t total_timestamp{0};
     183                 :            : 
     184                 :            :   std::unordered_map<std::string, UrlMsgInfo> url_map;
     185                 :            :   std::unordered_set<std::string> ser_map;
     186                 :            :   std::unordered_map<std::string, UrlMsgInfo> total_url_map;
     187                 :            :   std::unordered_map<std::string, SchemaData> total_schema_map;
     188                 :            : 
     189                 :            :   BagWriter::SplitCallback split_callback;
     190                 :            :   BagWriter::SchemaCallback schema_callback;
     191                 :            :   std::string split_filename;
     192                 :            :   std::mutex split_mtx;
     193                 :            : 
     194                 :            :   std::string app_name;
     195                 :            :   std::string tag_name;
     196                 :            :   int32_t timezone_diff{0};
     197                 :            : 
     198                 :            :   bool enable_compressed{false};
     199                 :            :   std::mutex write_mtx;
     200                 :            : 
     201                 :            :   ElapsedTimer cache_timer;
     202                 :            :   Timer check_timer;
     203                 :            : 
     204                 :            :   ElapsedTimer sync_timer;
     205                 :            : 
     206                 :            :   std::unordered_map<std::string, int64_t> compress_ignore_map;
     207                 :            :   std::unique_ptr<WriteStateSnapshot> cache_snapshot;
     208                 :            : 
     209                 :            :   std::string write_url_type;
     210                 :            : 
     211                 :            : // database
     212                 :            : #ifdef VLINK_ENABLE_SQLITE
     213                 :            :   ::sqlite3* db{nullptr};
     214                 :            :   ::sqlite3_stmt* schemas_stmt{nullptr};
     215                 :            :   ::sqlite3_stmt* datas_stmt{nullptr};
     216                 :            :   ::sqlite3_stmt* urls_stmt{nullptr};
     217                 :            :   ::sqlite3_stmt* update_complete_stmt{nullptr};
     218                 :            :   ::sqlite3_stmt* update_header_stmt{nullptr};
     219                 :            :   ::sqlite3_stmt* update_url_loss_stmt{nullptr};
     220                 :            :   ::sqlite3_stmt* update_url_meta_stmt{nullptr};
     221                 :            :   ::sqlite3_stmt* update_urls_stmt{nullptr};
     222                 :            : #endif
     223                 :            : 
     224                 :            :   // schema plugin interface
     225                 :            :   SchemaPluginInterface* schema_plugin_interface{nullptr};
     226                 :            : };
     227                 :            : 
     228                 :            : // VDBWriter
     229                 :        122 : VDBWriter::VDBWriter(const std::string& path, const Config& config)
     230         [ +  - ]:        122 :     : BagWriter(path, config), impl_(std::make_unique<Impl>()) {
     231   [ +  -  +  - ]:        122 :   set_name("VDBWriter");
     232                 :            : 
     233         [ +  - ]:        122 :   impl_->url_map.reserve(128);
     234         [ +  - ]:        122 :   impl_->ser_map.reserve(128);
     235   [ +  -  +  - ]:        122 :   url_loss_map_ref().reserve(128);
     236         [ +  - ]:        122 :   impl_->total_url_map.reserve(128);
     237   [ +  -  +  - ]:        122 :   total_url_loss_map_ref().reserve(128);
     238         [ +  - ]:        122 :   impl_->total_schema_map.reserve(128);
     239                 :            : 
     240         [ +  - ]:        122 :   impl_->schema_plugin_interface = get_schema_interface();
     241                 :            : 
     242   [ +  -  +  - ]:        122 :   impl_->app_name = get_default_app_name();
     243                 :            : 
     244         [ +  + ]:        122 :   if (config.tag_name.empty()) {
     245   [ +  -  +  - ]:         24 :     impl_->tag_name = get_default_tag_name();
     246                 :            :   } else {
     247         [ +  - ]:         98 :     impl_->tag_name = config.tag_name;
     248                 :            :   }
     249                 :            : 
     250         [ +  - ]:        122 :   impl_->timezone_diff = get_default_timezone_diff();
     251                 :            : 
     252         [ +  - ]:        122 :   impl_->path = path;
     253         [ +  - ]:        122 :   impl_->config = config;
     254                 :            : 
     255   [ +  +  +  + ]:        122 :   impl_->enable_compressed = impl_->config.compress == kCompressAuto || impl_->config.compress == kCompressLzav;
     256                 :            : 
     257         [ +  + ]:        122 :   if VUNLIKELY (impl_->config.max_task_depth <= 0) {
     258                 :          2 :     impl_->config.max_task_depth = BagWriter::Config().max_task_depth;
     259                 :            :   }
     260                 :            : 
     261         [ +  - ]:        122 :   reset_lockfree_capacity();
     262                 :            : 
     263         [ +  + ]:        122 :   if (!config.sync_mode) {
     264         [ +  - ]:         14 :     impl_->check_timer.attach(this);
     265         [ +  - ]:         14 :     impl_->check_timer.set_interval(kSyncWriteInterval);
     266   [ +  -  +  - ]:         14 :     impl_->check_timer.start([this]() {
     267                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     268                 :            :       if VUNLIKELY (impl_->quit_flag.load(std::memory_order_acquire)) {
     269                 :            :         return;
     270                 :            :       }
     271                 :            : 
     272                 :            :       if (impl_->cache_timer.get() > 3000) {
     273                 :            :         std::lock_guard lock(impl_->write_mtx);
     274                 :            :         sync_cache();
     275                 :            :       }
     276                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     277                 :            :     });
     278                 :            :   }
     279                 :            : 
     280                 :            :   try {
     281                 :            : #ifdef _WIN32
     282                 :            :     std::filesystem::path file_path(Helpers::string_to_wstring(path));
     283                 :            :     std::string suffix = Helpers::path_to_string(file_path.extension());
     284                 :            : #else
     285         [ +  - ]:        122 :     std::filesystem::path file_path(path);
     286   [ +  -  +  - ]:        122 :     std::string suffix = file_path.extension().string();
     287                 :            : #endif
     288                 :            : 
     289         [ +  - ]:        122 :     const auto parent_path = file_path.parent_path();
     290                 :            : 
     291                 :        643 :     std::transform(suffix.begin(), suffix.end(), suffix.begin(), [](unsigned char c) { return std::tolower(c); });
     292                 :            : 
     293         [ +  + ]:        122 :     if (suffix == ".vdbx") {
     294   [ +  -  +  + ]:         33 :       if (std::filesystem::exists(file_path)) {
     295                 :            :         try {
     296                 :          4 :           nlohmann::json root_json;
     297                 :            : 
     298                 :            :           {
     299         [ +  - ]:          4 :             std::ifstream filex(file_path);
     300                 :            : 
     301         [ +  + ]:          4 :             filex >> root_json;
     302                 :            : 
     303         [ +  - ]:          3 :             filex.close();
     304                 :          4 :           }
     305                 :            : 
     306   [ +  -  +  - ]:          3 :           nlohmann::json files_json = root_json["VLinkFiles"];
     307                 :            : 
     308   [ +  -  +  -  :         12 :           for (const auto& file_info : files_json) {
             +  -  +  + ]
     309         [ +  + ]:          9 :             if (!file_info.is_string()) {
     310                 :          4 :               continue;
     311                 :            :             }
     312                 :            : 
     313         [ +  - ]:          8 :             const auto stale_file_name = file_info.get<std::string>();
     314                 :            : #ifdef _WIN32
     315                 :            :             const std::filesystem::path stale_file_path(Helpers::string_to_wstring(stale_file_name));
     316                 :            : #else
     317         [ +  - ]:          8 :             const std::filesystem::path stale_file_path(stale_file_name);
     318                 :            : #endif
     319                 :            : 
     320   [ +  -  +  -  :         24 :             if (stale_file_path.empty() || stale_file_path == "." || stale_file_path == ".." ||
          +  -  +  -  +  
          -  +  +  +  -  
          +  +  -  -  -  
                      - ]
     321   [ +  -  +  -  :         16 :                 stale_file_path != stale_file_path.filename()) {
             +  -  -  - ]
     322   [ +  -  +  - ]:          6 :               CLOG_W("VDBWriter: Ignore unsafe split file path [%s].", stale_file_name.c_str());
     323                 :          3 :               continue;
     324                 :          3 :             }
     325                 :            : 
     326         [ +  - ]:          5 :             const auto stale_output_path = parent_path / stale_file_path;
     327                 :          5 :             std::error_code remove_ec;
     328                 :          5 :             std::filesystem::remove(stale_output_path, remove_ec);
     329                 :            : 
     330         [ +  + ]:          5 :             if VUNLIKELY (remove_ec) {
     331   [ +  -  +  -  :          2 :               CLOG_W("VDBWriter: Failed to remove stale split path [%s]: %s.", stale_file_name.c_str(),
                   +  - ]
     332                 :            :                      remove_ec.message().c_str());
     333                 :            :             }
     334   [ +  +  +  + ]:         11 :           }
     335                 :            : 
     336         [ +  - ]:          3 :           std::filesystem::remove(file_path);
     337         [ -  + ]:          5 :         } catch (const nlohmann::json::exception& e) {
     338   [ +  -  +  - ]:          2 :           CLOG_W("VDBWriter: Failed to parse stale split manifest [%s]: %s.", path.c_str(), e.what());
     339                 :          1 :         }
     340                 :            :       }
     341                 :            : 
     342                 :         33 :       impl_->is_split_mode.store(true, std::memory_order_release);
     343                 :         33 :       impl_->split_index.store(0, std::memory_order_relaxed);
     344                 :            : 
     345                 :            : #ifdef _WIN32
     346                 :            : 
     347                 :            :       std::error_code absolute_ec;
     348                 :            :       auto absolute_path = std::filesystem::absolute(file_path, absolute_ec);
     349                 :            : 
     350                 :            :       if VUNLIKELY (absolute_ec) {
     351                 :            :         absolute_path = file_path;
     352                 :            :       }
     353                 :            : 
     354                 :            :       impl_->path = Helpers::path_to_string(absolute_path);
     355                 :            :       impl_->split_output_dir = absolute_path.parent_path();
     356                 :            : 
     357                 :            :       if (parent_path.empty()) {
     358                 :            :         impl_->base_dir.clear();
     359                 :            :         impl_->base_name = Helpers::path_to_string(file_path.stem());
     360                 :            :       } else {
     361                 :            :         impl_->base_dir = Helpers::path_to_string(parent_path);
     362                 :            :         impl_->base_name = Helpers::path_to_string(std::filesystem::path(parent_path / file_path.stem()));
     363                 :            :       }
     364                 :            : #else
     365                 :            : 
     366                 :         33 :       std::error_code absolute_ec;
     367         [ +  - ]:         33 :       auto absolute_path = std::filesystem::absolute(file_path, absolute_ec);
     368                 :            : 
     369         [ -  + ]:         33 :       if VUNLIKELY (absolute_ec) {
     370         [ #  # ]:          0 :         absolute_path = file_path;
     371                 :            :       }
     372                 :            : 
     373         [ +  - ]:         33 :       impl_->path = absolute_path.string();
     374         [ +  - ]:         33 :       impl_->split_output_dir = absolute_path.parent_path();
     375                 :            : 
     376         [ +  + ]:         33 :       if (parent_path.empty()) {
     377                 :          3 :         impl_->base_dir.clear();
     378   [ +  -  +  - ]:          3 :         impl_->base_name = file_path.stem().string();
     379                 :            :       } else {
     380         [ +  - ]:         30 :         impl_->base_dir = parent_path.string();
     381   [ +  -  +  -  :         30 :         impl_->base_name = std::filesystem::path(parent_path / file_path.stem()).string();
                   +  - ]
     382                 :            :       }
     383                 :            : #endif
     384                 :            : 
     385         [ +  - ]:         33 :       impl_->time_start = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
     386                 :         33 :       impl_->time_current = impl_->time_start;
     387                 :            : 
     388         [ +  + ]:         33 :       if (impl_->config.start_timestamp > 0) {
     389                 :         27 :         impl_->start_timestamp = impl_->config.start_timestamp;
     390                 :            :       } else {
     391                 :          6 :         impl_->start_timestamp = impl_->time_start.time_since_epoch().count();
     392                 :            :       }
     393                 :            : 
     394         [ +  - ]:         33 :       write_filex(false);
     395                 :            : 
     396         [ +  + ]:         33 :       if (impl_->config.split_name_by_time) {
     397         [ +  + ]:          3 :         if (impl_->base_dir.empty()) {
     398   [ +  -  +  - ]:          2 :           impl_->split_filename = get_format_date(&impl_->time_current, true) + ".vdb";
     399                 :            :         } else {
     400   [ +  -  +  -  :          1 :           impl_->split_filename = impl_->base_dir + "/" + get_format_date(&impl_->time_current, true) + ".vdb";
             +  -  +  - ]
     401                 :            :         }
     402                 :            :       } else {
     403                 :         30 :         impl_->split_filename =
     404   [ +  -  +  -  :         90 :             impl_->base_name + "." + std::to_string(impl_->split_index.load(std::memory_order_relaxed) + 1) + ".vdb";
             +  -  +  - ]
     405                 :            :       }
     406                 :            : 
     407         [ +  - ]:         33 :       open_split(impl_->split_filename);
     408                 :         33 :     } else {
     409         [ +  - ]:         89 :       impl_->time_start = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
     410                 :         89 :       impl_->time_current = impl_->time_start;
     411                 :            : 
     412         [ +  + ]:         89 :       if (impl_->config.start_timestamp > 0) {
     413                 :         74 :         impl_->start_timestamp = impl_->config.start_timestamp;
     414                 :            :       } else {
     415                 :         15 :         impl_->start_timestamp = impl_->time_start.time_since_epoch().count();
     416                 :            :       }
     417                 :            : 
     418                 :         89 :       impl_->is_split_mode.store(false, std::memory_order_release);
     419                 :         89 :       impl_->split_index.store(0, std::memory_order_relaxed);
     420                 :            : 
     421         [ +  - ]:         89 :       open(path);
     422                 :            :     }
     423         [ -  - ]:        122 :   } catch (std::filesystem::filesystem_error& e) {
     424                 :            :     VLOG_F("VDBWriter: Filesystem error during init, ", e.what(), ".");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     425                 :            :     return;                                                              // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     426                 :            :   }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     427                 :            : 
     428                 :        122 :   impl_->elapsed_timer.start();
     429                 :        122 :   impl_->sync_timer.start();
     430                 :            : 
     431                 :            : #ifndef VLINK_ENABLE_SQLITE
     432                 :            :   VLOG_F("VDBWriter: The compile macro VLINK_ENABLE_SQLITE is not turned on.");
     433                 :            : #endif
     434                 :            : }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     435                 :            : 
     436                 :        125 : VDBWriter::~VDBWriter() {
     437                 :        122 :   detach_plugin();
     438                 :            : 
     439                 :        122 :   impl_->quit_flag.store(true, std::memory_order_release);
     440                 :            : 
     441                 :        122 :   impl_->check_timer.stop();
     442                 :            : 
     443         [ -  + ]:        122 :   if VUNLIKELY (!wait_for_idle(30000U)) {
     444                 :            :     VLOG_W("VDBWriter: Force to quit.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     445                 :            :   }
     446                 :            : 
     447                 :            : #ifdef VLINK_ENABLE_SQLITE
     448                 :            : 
     449         [ +  + ]:        122 :   if VLIKELY (impl_->db) {
     450                 :        103 :     ::sqlite3_interrupt(impl_->db);
     451                 :            :   }
     452                 :            : #endif
     453                 :            : 
     454                 :        122 :   quit(true);
     455                 :            : 
     456                 :        122 :   wait_for_quit();
     457                 :            : 
     458                 :        122 :   close();
     459                 :        125 : }
     460                 :            : 
     461                 :        142 : void VDBWriter::close() {
     462                 :        142 :   close_segment();
     463                 :            : 
     464   [ +  +  -  +  :        142 :   if VUNLIKELY (impl_->is_split_mode.load(std::memory_order_acquire) && !write_filex(true)) {
                   -  + ]
     465                 :            :     set_fail();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     466                 :            :   }
     467                 :        142 : }
     468                 :            : 
     469                 :         24 : void VDBWriter::register_split_callback(SplitCallback&& callback, bool before) {
     470         [ +  - ]:         24 :   std::lock_guard lock(impl_->split_mtx);
     471                 :         24 :   impl_->split_before = before;
     472                 :         24 :   impl_->split_callback = std::move(callback);
     473                 :         24 : }
     474                 :            : 
     475                 :          5 : void VDBWriter::register_schema_callback(SchemaCallback&& callback) {
     476         [ +  - ]:          5 :   std::lock_guard lock(impl_->write_mtx);
     477                 :          5 :   impl_->schema_callback = std::move(callback);
     478                 :          5 : }
     479                 :            : 
     480                 :         41 : bool VDBWriter::merge_schema(SchemaData& schema_data) {
     481                 :            :   const auto resolved_schema_type =
     482                 :         41 :       SchemaData::resolve_type(schema_data.schema_type, schema_data.name, schema_data.encoding);
     483                 :         41 :   schema_data.schema_type = resolved_schema_type;
     484                 :            : 
     485         [ +  + ]:         41 :   if VUNLIKELY (schema_data.name.empty()) {
     486                 :          1 :     return true;
     487                 :            :   }
     488                 :            : 
     489   [ +  +  +  +  :         40 :   if (schema_data.encoding.empty() && SchemaData::is_real_type(resolved_schema_type)) {
                   +  + ]
     490         [ +  - ]:          3 :     schema_data.encoding = std::string(SchemaData::convert_type(resolved_schema_type));
     491                 :            :   }
     492                 :            : 
     493         [ +  - ]:         40 :   std::string schema_key = schema_data.name;
     494         [ +  - ]:         40 :   schema_key.push_back('\x1F');
     495         [ +  - ]:         40 :   schema_key.append(SchemaData::convert_type(resolved_schema_type));
     496                 :            : 
     497                 :         40 :   std::string unknown_schema_key;
     498         [ +  - ]:         40 :   auto schema_iter = impl_->total_schema_map.find(schema_key);
     499                 :            : 
     500   [ +  +  +  +  :         40 :   if (schema_iter == impl_->total_schema_map.end() && SchemaData::is_real_type(resolved_schema_type)) {
                   +  + ]
     501         [ +  - ]:         36 :     unknown_schema_key = schema_data.name;
     502         [ +  - ]:         36 :     unknown_schema_key.push_back('\x1F');
     503         [ +  - ]:         36 :     schema_iter = impl_->total_schema_map.find(unknown_schema_key);
     504                 :            :   }
     505                 :            : 
     506         [ +  + ]:         40 :   if (schema_iter == impl_->total_schema_map.end()) {
     507   [ +  +  +  -  :         71 :     if (!schema_data.encoding.empty() && !schema_data.data.empty() &&
                   +  - ]
     508   [ +  -  +  + ]:         71 :         impl_->ser_map.find(schema_key) == impl_->ser_map.end()) {
     509   [ +  -  -  + ]:         35 :       if VUNLIKELY (!insert_schema(schema_data)) {
     510                 :            :         return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     511                 :            :       }
     512                 :            : 
     513         [ +  - ]:         35 :       impl_->ser_map.emplace(schema_key);
     514                 :            :     }
     515                 :            : 
     516         [ +  - ]:         36 :     impl_->total_schema_map.emplace(schema_key, schema_data);
     517                 :            : 
     518                 :         36 :     return true;
     519                 :            :   }
     520                 :            : 
     521                 :          4 :   const auto& current = schema_iter->second;
     522                 :            : 
     523   [ +  -  +  +  :          4 :   if VUNLIKELY ((!schema_data.encoding.empty() && !current.encoding.empty() &&
          +  +  -  +  +  
          -  +  -  +  +  
          +  +  +  +  +  
          +  +  +  +  -  
          +  +  +  +  -  
             +  -  +  +  
                      + ]
     524                 :            :                  current.encoding != schema_data.encoding) ||
     525                 :            :                 (!schema_data.data.empty() && !current.data.empty() && current.data != schema_data.data) ||
     526                 :            :                 (SchemaData::is_real_type(resolved_schema_type) && SchemaData::is_real_type(current.schema_type) &&
     527                 :            :                  current.schema_type != resolved_schema_type)) {
     528   [ +  -  +  - ]:          4 :     CLOG_E("VDBWriter: Conflicting schema pushed for [%s].", schema_data.name.c_str());
     529                 :          2 :     return false;
     530                 :            :   }
     531                 :            : 
     532         [ +  - ]:          2 :   SchemaData merged_schema = current;
     533                 :            : 
     534   [ +  +  +  -  :          2 :   if (merged_schema.encoding.empty() && !schema_data.encoding.empty()) {
                   +  + ]
     535         [ +  - ]:          1 :     merged_schema.encoding = schema_data.encoding;
     536                 :            :   }
     537                 :            : 
     538   [ +  +  +  -  :          2 :   if (merged_schema.data.empty() && !schema_data.data.empty()) {
                   +  + ]
     539                 :          1 :     merged_schema.data = schema_data.data;
     540                 :            :   }
     541                 :            : 
     542   [ +  +  +  -  :          2 :   if (!SchemaData::is_real_type(merged_schema.schema_type) && SchemaData::is_real_type(resolved_schema_type)) {
                   +  + ]
     543                 :          1 :     merged_schema.schema_type = resolved_schema_type;
     544                 :            :   }
     545                 :            : 
     546   [ +  -  +  -  :          4 :   if (!merged_schema.encoding.empty() && !merged_schema.data.empty() &&
                   +  + ]
     547   [ +  -  +  + ]:          4 :       impl_->ser_map.find(schema_key) == impl_->ser_map.end()) {
     548   [ +  -  -  + ]:          1 :     if VUNLIKELY (!insert_schema(merged_schema)) {
     549                 :            :       return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     550                 :            :     }
     551                 :            : 
     552         [ +  - ]:          1 :     impl_->ser_map.emplace(schema_key);
     553                 :            :   }
     554                 :            : 
     555         [ +  - ]:          2 :   schema_data = merged_schema;
     556                 :            : 
     557   [ +  +  +  -  :          2 :   if (schema_iter->first != schema_key && merged_schema.schema_type == resolved_schema_type) {
                   +  + ]
     558         [ +  - ]:          1 :     impl_->total_schema_map.erase(schema_iter);
     559         [ +  - ]:          1 :     impl_->total_schema_map.emplace(schema_key, schema_data);
     560                 :            :   } else {
     561         [ +  - ]:          1 :     schema_iter->second = schema_data;
     562                 :            :   }
     563                 :            : 
     564                 :          2 :   return true;
     565                 :         40 : }
     566                 :            : 
     567                 :        242 : bool VDBWriter::load_schema(const std::string& ser_type, SchemaType& schema_type, SchemaData& schema_data) {
     568                 :            : #ifdef VLINK_ENABLE_SQLITE
     569                 :        242 :   schema_data = SchemaData{};
     570                 :            : 
     571         [ -  + ]:        242 :   if VUNLIKELY (ser_type.empty()) {
     572                 :            :     return true;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     573                 :            :   }
     574                 :            : 
     575         [ +  - ]:        242 :   std::string schema_key = ser_type;
     576         [ +  - ]:        242 :   schema_key.push_back('\x1F');
     577         [ +  - ]:        242 :   schema_key.append(SchemaData::convert_type(schema_type));
     578                 :            : 
     579                 :        242 :   std::string unknown_schema_key;
     580                 :        242 :   auto schema_iter = impl_->total_schema_map.end();
     581                 :            : 
     582         [ +  + ]:        242 :   if (schema_type != SchemaType::kUnknown) {
     583         [ +  - ]:        211 :     schema_iter = impl_->total_schema_map.find(schema_key);
     584                 :            : 
     585         [ +  + ]:        211 :     if (schema_iter == impl_->total_schema_map.end()) {
     586         [ +  - ]:        172 :       unknown_schema_key = ser_type;
     587         [ +  - ]:        172 :       unknown_schema_key.push_back('\x1F');
     588         [ +  - ]:        172 :       schema_iter = impl_->total_schema_map.find(unknown_schema_key);
     589                 :            :     }
     590                 :            :   } else {
     591   [ +  -  +  - ]:         62 :     const auto prefix = ser_type + std::string("\x1F");
     592                 :            : 
     593         [ +  + ]:         62 :     for (auto iter = impl_->total_schema_map.begin(); iter != impl_->total_schema_map.end(); ++iter) {
     594         [ +  + ]:         31 :       if (!Helpers::has_startwith(iter->first, prefix)) {
     595                 :         30 :         continue;
     596                 :            :       }
     597                 :            : 
     598         [ -  + ]:          1 :       if (schema_iter != impl_->total_schema_map.end()) {
     599                 :            :         schema_iter = impl_->total_schema_map.end();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     600                 :            :         break;                                        // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     601                 :            :       }
     602                 :            : 
     603                 :          1 :       schema_iter = iter;
     604                 :            :     }
     605                 :         31 :   }
     606                 :            : 
     607         [ +  + ]:        242 :   if VLIKELY (schema_iter != impl_->total_schema_map.end()) {
     608         [ +  - ]:         40 :     schema_data.name = schema_iter->second.name;
     609         [ +  - ]:         40 :     schema_data.encoding = schema_iter->second.encoding;
     610                 :         40 :     schema_data.schema_type = schema_iter->second.schema_type;
     611                 :         40 :     schema_data.data.shallow_copy(schema_iter->second.data);
     612         [ -  + ]:        202 :   } else if (impl_->schema_plugin_interface) {
     613                 :            :     schema_data =
     614                 :            :         impl_->schema_plugin_interface->search_schema(ser_type, schema_type);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     615         [ +  + ]:        202 :   } else if (impl_->schema_callback) {
     616         [ +  - ]:          8 :     schema_data = impl_->schema_callback(ser_type, schema_type);
     617                 :            :   }
     618                 :            : 
     619                 :        242 :   schema_type = SchemaData::resolve_type(schema_type, ser_type, schema_data.encoding);
     620                 :        242 :   schema_data.schema_type = SchemaData::resolve_type(schema_data.schema_type, ser_type, schema_data.encoding);
     621                 :            : 
     622   [ +  +  +  - ]:        242 :   if (schema_type != SchemaType::kUnknown && schema_data.schema_type != SchemaType::kUnknown &&
     623         [ +  + ]:        212 :       schema_type != schema_data.schema_type) {
     624   [ +  -  +  - ]:          4 :     CLOG_E("VDBWriter: Schema family mismatch for [%s], requested = %d, resolved = %d.", ser_type.c_str(),
     625                 :            :            static_cast<int>(schema_type), static_cast<int>(schema_data.schema_type));
     626                 :          2 :     return false;
     627                 :            :   }
     628                 :            : 
     629   [ +  +  +  +  :        240 :   if (schema_type != SchemaType::kUnknown && schema_data.encoding.empty()) {
                   +  + ]
     630         [ +  - ]:        163 :     schema_data.encoding = std::string(SchemaData::convert_type(schema_type));
     631                 :            :   }
     632                 :            : 
     633   [ +  +  -  + ]:        240 :   if (schema_data.schema_type == SchemaType::kUnknown && schema_type != SchemaType::kUnknown) {
     634                 :            :     schema_data.schema_type = schema_type;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     635                 :            :   }
     636                 :            : 
     637         [ +  + ]:        240 :   if (!schema_data.name.empty()) {
     638         [ +  - ]:         47 :     std::string resolved_schema_key = ser_type;
     639         [ +  - ]:         47 :     resolved_schema_key.push_back('\x1F');
     640         [ +  - ]:         47 :     resolved_schema_key.append(SchemaData::convert_type(schema_data.schema_type));
     641                 :            : 
     642   [ +  +  +  -  :         47 :     if VLIKELY (schema_iter != impl_->total_schema_map.end() && schema_iter->first == resolved_schema_key) {
                   +  + ]
     643   [ -  +  -  -  :         40 :       if (schema_iter->second.encoding.empty() && !schema_data.encoding.empty()) {
                   -  + ]
     644         [ #  # ]:          0 :         schema_iter->second.encoding = schema_data.encoding;
     645                 :            :       }
     646                 :            : 
     647                 :         40 :       schema_iter->second.schema_type = schema_data.schema_type;
     648                 :            :     } else {
     649         [ +  - ]:          7 :       SchemaData stored_schema = schema_data;
     650                 :            : 
     651   [ -  +  -  -  :          7 :       if (schema_iter != impl_->total_schema_map.end() && schema_iter->second.schema_type == SchemaType::kUnknown &&
                   -  + ]
     652         [ #  # ]:          0 :           schema_data.schema_type != SchemaType::kUnknown) {
     653                 :            :         impl_->total_schema_map.erase(schema_iter);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     654                 :            :       }
     655                 :            : 
     656                 :            :       const auto stored_iter =
     657         [ +  - ]:          7 :           impl_->total_schema_map.insert_or_assign(resolved_schema_key, std::move(stored_schema)).first;
     658                 :          7 :       schema_data.data.shallow_copy(stored_iter->second.data);
     659                 :          7 :     }
     660                 :         47 :   }
     661                 :            : 
     662                 :        240 :   return true;
     663                 :            : #else
     664                 :            :   (void)ser_type;
     665                 :            :   (void)schema_type;
     666                 :            :   (void)schema_data;
     667                 :            :   return false;
     668                 :            : #endif
     669                 :        242 : }
     670                 :            : 
     671                 :         41 : bool VDBWriter::push_schema(const SchemaData& schema_data) {
     672         [ +  - ]:         41 :   SchemaData stored_schema = schema_data;
     673                 :            : 
     674         [ +  + ]:         41 :   if (impl_->config.sync_mode) {
     675         [ +  - ]:         38 :     std::lock_guard lock(impl_->write_mtx);
     676         [ +  - ]:         38 :     return merge_schema(stored_schema);
     677                 :         38 :   }
     678                 :            : 
     679   [ +  -  +  - ]:          3 :   bool posted = post_persistent_task([this, stored_schema = std::move(stored_schema)]() mutable {
     680         [ +  - ]:          3 :     std::lock_guard lock(impl_->write_mtx);
     681                 :            : 
     682   [ +  -  -  + ]:          3 :     if VUNLIKELY (!merge_schema(stored_schema)) {
     683   [ #  #  #  # ]:          0 :       CLOG_E("VDBWriter: Deferred merge_schema failed for [%s] in async push_schema path.", stored_schema.name.c_str());
     684                 :          0 :       set_fail();
     685                 :            :     }
     686                 :          3 :   });
     687                 :            : 
     688                 :          3 :   return posted;
     689                 :         41 : }
     690                 :            : 
     691                 :        350 : int64_t VDBWriter::record(const Frame& frame, int64_t timestamp) {
     692                 :            : #ifdef VLINK_ENABLE_SQLITE
     693                 :            : 
     694                 :        350 :   const std::string& url = frame.url;
     695                 :        350 :   const std::string& ser_type = frame.ser_type;
     696                 :        350 :   const SchemaType schema_type = frame.schema_type;
     697                 :        350 :   const ActionType action_type = frame.action_type;
     698                 :        350 :   const Bytes& data = frame.data;
     699                 :        350 :   const int64_t microseconds_timestamp = timestamp;
     700                 :            : 
     701         [ +  + ]:        350 :   if (impl_->config.sync_mode) {
     702         [ +  - ]:        336 :     std::lock_guard lock(impl_->write_mtx);
     703                 :            : 
     704   [ +  -  +  + ]:        336 :     if VUNLIKELY (!write(url, ser_type, schema_type, action_type, data, microseconds_timestamp)) {
     705                 :          6 :       return -1;
     706                 :            :     }
     707         [ +  + ]:        336 :   } else {
     708         [ +  + ]:         28 :     if VUNLIKELY (impl_->memory_size.load(std::memory_order_relaxed) + static_cast<int64_t>(data.size()) >
     709                 :            :                   impl_->config.max_memory_size) {
     710   [ +  -  +  - ]:          2 :       CLOG_E("The memory data in the queue exceeds %.1fGB and the task is automatically discarded.",
     711                 :            :              impl_->config.max_memory_size / 1024.0 / 1024.0 / 1024.0);
     712                 :            : 
     713                 :          2 :       return -1;
     714                 :            :     }
     715                 :            : 
     716                 :         13 :     int url_index = -1;
     717                 :         13 :     int ser_index = -1;
     718                 :            : 
     719         [ +  - ]:         13 :     get_url_meta(url, ser_type, url_index, ser_index);
     720                 :            : 
     721                 :         13 :     const auto queued_size = static_cast<int64_t>(data.size());
     722                 :            : 
     723                 :         13 :     impl_->memory_size.fetch_add(queued_size, std::memory_order_relaxed);
     724                 :         13 :     Impl::MemoryCharge memory_charge(impl_->memory_size, queued_size);
     725                 :            : 
     726   [ +  -  +  - ]:         26 :     bool posted = post_persistent_task([this, url_index, ser_index, schema_type, action_type, data,
     727                 :         13 :                                         memory_charge = std::move(memory_charge),
     728                 :            :                                         microseconds_timestamp]() {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     729                 :            :       (void)memory_charge;
     730                 :            : 
     731                 :         11 :       std::string url;
     732                 :         11 :       std::string ser_type;
     733                 :            : 
     734         [ +  - ]:         11 :       std::lock_guard lock(impl_->write_mtx);
     735         [ +  - ]:         11 :       get_url_meta(url_index, ser_index, url, ser_type);
     736                 :            : 
     737   [ +  -  +  + ]:         11 :       if VUNLIKELY (!write(url, ser_type, schema_type, action_type, data, microseconds_timestamp)) {
     738                 :          1 :         set_fail();
     739                 :            :       }
     740                 :         11 :     });
     741                 :            : 
     742         [ +  + ]:         13 :     if VUNLIKELY (!posted) {
     743                 :            :       return -1;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     744                 :            :     }
     745         [ +  + ]:         13 :   }
     746                 :            : 
     747                 :        342 :   return microseconds_timestamp;
     748                 :            : #else
     749                 :            :   (void)frame;
     750                 :            :   (void)timestamp;
     751                 :            :   return -1;
     752                 :            : #endif
     753                 :            : }
     754                 :            : 
     755                 :          1 : int64_t VDBWriter::get_record_timestamp() const { return impl_->elapsed_timer.get(); }
     756                 :            : 
     757                 :         30 : bool VDBWriter::is_dumping() const { return impl_->is_dumping.load(std::memory_order_relaxed); }
     758                 :            : 
     759                 :         60 : bool VDBWriter::is_split_mode() const { return impl_->is_split_mode.load(std::memory_order_acquire); }
     760                 :            : 
     761                 :        140 : int VDBWriter::get_split_index() const { return impl_->split_index.load(std::memory_order_relaxed); }
     762                 :            : 
     763                 :         17 : size_t VDBWriter::get_max_task_count() const { return impl_->config.max_task_depth; }
     764                 :            : 
     765                 :         11 : void VDBWriter::on_begin() {
     766                 :         11 :   MessageLoop::on_begin();
     767                 :            : 
     768                 :         11 :   impl_->elapsed_timer.restart();
     769                 :         11 :   impl_->sync_timer.restart();
     770                 :         11 : }
     771                 :            : 
     772                 :         11 : void VDBWriter::on_end() { MessageLoop::on_end(); }
     773                 :            : 
     774                 :        151 : void VDBWriter::open(const std::string& path) {
     775                 :            : #ifdef VLINK_ENABLE_SQLITE
     776                 :            :   try {
     777                 :            : #ifdef _WIN32
     778                 :            :     impl_->split_file_list.emplace_back(Helpers::path_to_string(std::filesystem::path(path).filename()));
     779                 :            :     std::filesystem::path file_path(Helpers::string_to_wstring(path));
     780                 :            : #else
     781   [ +  -  +  -  :        151 :     impl_->split_file_list.emplace_back(std::filesystem::path(path).filename().string());
             +  -  +  - ]
     782         [ +  - ]:        151 :     std::filesystem::path file_path(path);
     783                 :            : #endif
     784                 :            : 
     785   [ +  -  +  + ]:        151 :     if (std::filesystem::exists(file_path)) {
     786         [ +  - ]:          1 :       std::filesystem::remove(file_path);
     787                 :            :     } else {
     788         [ +  - ]:        150 :       auto parent_path = file_path.parent_path();
     789                 :            : 
     790   [ +  -  +  -  :        150 :       if (!parent_path.empty() && !std::filesystem::exists(parent_path)) {
             +  +  +  + ]
     791         [ +  - ]:          1 :         std::filesystem::create_directories(parent_path);
     792                 :            :       }
     793                 :        150 :     }
     794         [ -  - ]:        151 :   } catch (std::filesystem::filesystem_error& e) {
     795                 :            :     VLOG_F("VDBWriter: Filesystem error during file preparation, ", e.what(), ".");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     796                 :            :     return;                                                                          // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     797                 :            :   }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     798                 :            : 
     799                 :        151 :   int ret = 0;
     800                 :        151 :   char* err_msg = nullptr;
     801                 :            : 
     802                 :        151 :   auto free_err_msg = [&err_msg]() noexcept {
     803         [ -  + ]:        151 :     if (err_msg) {
     804                 :            :       ::sqlite3_free(err_msg);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     805                 :            :       err_msg = nullptr;        // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     806                 :            :     }
     807                 :        302 :   };
     808                 :            : 
     809                 :        151 :   auto finalize_stmt = [](sqlite3_stmt*& stmt) noexcept {
     810         [ +  - ]:        151 :     if (stmt) {
     811                 :        151 :       ::sqlite3_finalize(stmt);
     812                 :        151 :       stmt = nullptr;
     813                 :            :     }
     814                 :        151 :   };
     815                 :            : 
     816                 :          0 :   auto close_db = [this, &finalize_stmt]() noexcept {
     817                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     818                 :            :     if (!impl_->db) {
     819                 :            :       return;
     820                 :            :     }
     821                 :            : 
     822                 :            :     if (impl_->in_cached.load(std::memory_order_relaxed)) {
     823                 :            :       ::sqlite3_exec(impl_->db, "ROLLBACK;", nullptr, nullptr, nullptr);
     824                 :            :       impl_->in_cached.store(false, std::memory_order_relaxed);
     825                 :            :       impl_->cached_size.store(0, std::memory_order_relaxed);
     826                 :            :       impl_->cache_timer.stop();
     827                 :            :     }
     828                 :            : 
     829                 :            :     finalize_stmt(impl_->schemas_stmt);
     830                 :            :     finalize_stmt(impl_->datas_stmt);
     831                 :            :     finalize_stmt(impl_->urls_stmt);
     832                 :            :     finalize_stmt(impl_->update_complete_stmt);
     833                 :            :     finalize_stmt(impl_->update_header_stmt);
     834                 :            :     finalize_stmt(impl_->update_url_loss_stmt);
     835                 :            :     finalize_stmt(impl_->update_url_meta_stmt);
     836                 :            :     finalize_stmt(impl_->update_urls_stmt);
     837                 :            : 
     838                 :            :     const char* err_ptr = ::sqlite3_errmsg(impl_->db);
     839                 :            :     std::string close_err = err_ptr ? err_ptr : std::string{};
     840                 :            :     int close_ret = ::sqlite3_close_v2(impl_->db);
     841                 :            : 
     842                 :            :     if VUNLIKELY (close_ret != SQLITE_OK) {
     843                 :            :       CLOG_W("Failed to close database (rc=%d): %s.", close_ret, close_err.c_str());
     844                 :            :     }
     845                 :            : 
     846                 :            :     impl_->db = nullptr;
     847                 :            :   };
     848                 :            :   // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     849                 :            : 
     850                 :            :   // open db
     851         [ +  - ]:        151 :   ret = ::sqlite3_open_v2(path.c_str(), &impl_->db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr);
     852                 :            : 
     853         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
     854                 :            :     CLOG_F("Failed to open database [%s].", path.c_str());  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     855                 :            :     close_db();                                             // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     856                 :            :     return;                                                 // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     857                 :            :   }
     858                 :            : 
     859                 :            :   // opt busy_timeout
     860         [ +  - ]:        151 :   ::sqlite3_busy_timeout(impl_->db, 100);
     861                 :            : 
     862                 :            :   // opt sqlite temp_store
     863         [ +  - ]:        151 :   ret = ::sqlite3_exec(impl_->db, "PRAGMA temp_store = MEMORY;", nullptr, nullptr, &err_msg);
     864                 :            : 
     865         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
     866                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     867                 :            :     CLOG_F("Failed to set temp_store: %s.", err_msg);
     868                 :            : 
     869                 :            :     free_err_msg();
     870                 :            :     close_db();
     871                 :            : 
     872                 :            :     return;
     873                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     874                 :            :   }
     875                 :            : 
     876                 :            :   // opt sqlite page_size
     877         [ +  - ]:        151 :   ret = ::sqlite3_exec(impl_->db, "PRAGMA page_size = 16384;", nullptr, nullptr, &err_msg);
     878                 :            : 
     879         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
     880                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     881                 :            :     CLOG_F("Failed to set page_size: %s.", err_msg);
     882                 :            : 
     883                 :            :     free_err_msg();
     884                 :            :     close_db();
     885                 :            : 
     886                 :            :     return;
     887                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     888                 :            :   }
     889                 :            : 
     890                 :            :   // opt sqlite cache_size
     891         [ +  - ]:        151 :   ret = ::sqlite3_exec(impl_->db, "PRAGMA cache_size = 8192;", nullptr, nullptr, &err_msg);
     892                 :            : 
     893         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
     894                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     895                 :            :     CLOG_F("Failed to set cache_size: %s.", err_msg);
     896                 :            : 
     897                 :            :     free_err_msg();
     898                 :            :     close_db();
     899                 :            : 
     900                 :            :     return;
     901                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     902                 :            :   }
     903                 :            : 
     904                 :            :   // opt sqlite synchronous
     905         [ +  - ]:        151 :   ret = ::sqlite3_exec(impl_->db, "PRAGMA synchronous = OFF;", nullptr, nullptr, &err_msg);
     906                 :            : 
     907         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
     908                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     909                 :            :     CLOG_F("Failed to set synchronous: %s.", err_msg);
     910                 :            : 
     911                 :            :     free_err_msg();
     912                 :            :     close_db();
     913                 :            : 
     914                 :            :     return;
     915                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     916                 :            :   }
     917                 :            : 
     918                 :            :   // opt sqlite journal_mode
     919                 :            : 
     920         [ +  + ]:        151 :   if (impl_->config.wal_mode) {
     921         [ +  - ]:          2 :     ret = ::sqlite3_exec(impl_->db, "PRAGMA journal_mode = WAL;", nullptr, nullptr, &err_msg);
     922         [ -  + ]:          2 :     if VUNLIKELY (ret != SQLITE_OK) {
     923                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     924                 :            :       CLOG_F("Failed to set journal_mode: %s.", err_msg);
     925                 :            : 
     926                 :            :       free_err_msg();
     927                 :            :       close_db();
     928                 :            : 
     929                 :            :       return;
     930                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     931                 :            :     }
     932                 :            :   } else {
     933         [ +  - ]:        149 :     ret = ::sqlite3_exec(impl_->db, "PRAGMA journal_mode = OFF;", nullptr, nullptr, &err_msg);
     934         [ -  + ]:        149 :     if VUNLIKELY (ret != SQLITE_OK) {
     935                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     936                 :            :       CLOG_F("Failed to set journal_mode: %s.", err_msg);
     937                 :            : 
     938                 :            :       free_err_msg();
     939                 :            :       close_db();
     940                 :            : 
     941                 :            :       return;
     942                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     943                 :            :     }
     944                 :            :   }
     945                 :            : 
     946                 :            :   // opt sqlite automatic_index
     947         [ +  - ]:        151 :   ret = ::sqlite3_exec(impl_->db, "PRAGMA automatic_index = OFF;", nullptr, nullptr, &err_msg);
     948                 :            : 
     949         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
     950                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     951                 :            :     CLOG_F("Failed to set automatic_index: %s.", err_msg);
     952                 :            : 
     953                 :            :     free_err_msg();
     954                 :            :     close_db();
     955                 :            : 
     956                 :            :     return;
     957                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     958                 :            :   }
     959                 :            : 
     960                 :            :   // opt sqlite locking_mode
     961         [ +  - ]:        151 :   ret = ::sqlite3_exec(impl_->db, "PRAGMA locking_mode = EXCLUSIVE;", nullptr, nullptr, &err_msg);
     962                 :            : 
     963         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
     964                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     965                 :            :     CLOG_F("Failed to set locking_mode: %s.", err_msg);
     966                 :            : 
     967                 :            :     free_err_msg();
     968                 :            :     close_db();
     969                 :            : 
     970                 :            :     return;
     971                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     972                 :            :   }
     973                 :            : 
     974                 :            :   // set busy_timeout
     975         [ +  - ]:        151 :   ::sqlite3_busy_timeout(impl_->db, 500);
     976                 :            : 
     977         [ +  - ]:        151 :   begin_cache();
     978                 :            : 
     979                 :            :   // create header table
     980         [ +  - ]:        151 :   ret = ::sqlite3_exec(impl_->db,
     981                 :            :                        "CREATE TABLE IF NOT EXISTS VLinkHeader(major INTEGER, minor INTEGER, patch INTEGER, "
     982                 :            :                        "count INTEGER, duration INTEGER, accuracy TEXT, compress TEXT, process TEXT, date TEXT, "
     983                 :            :                        "tag TEXT, complete INTEGER, timezone INTEGER, start_timestamp INTEGER);",
     984                 :            :                        nullptr, nullptr, &err_msg);
     985                 :            : 
     986         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
     987                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     988                 :            :     CLOG_F("Failed to create header table: %s.", err_msg);
     989                 :            : 
     990                 :            :     free_err_msg();
     991                 :            :     close_db();
     992                 :            : 
     993                 :            :     return;
     994                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     995                 :            :   }
     996                 :            : 
     997                 :            :   // prepare header table
     998                 :        151 :   ::sqlite3_stmt* header_stmt = nullptr;
     999         [ +  - ]:        151 :   ret = ::sqlite3_prepare_v2(
    1000                 :        151 :       impl_->db,
    1001                 :            :       "INSERT INTO VLinkHeader (major, minor, patch, count, duration, accuracy, compress, "
    1002                 :            :       "process, date, tag, complete, timezone, start_timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);",
    1003                 :            :       -1, &header_stmt, nullptr);
    1004                 :            : 
    1005         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
    1006                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
    1007                 :            :     CLOG_F("Failed to prepare header table: %s.", ::sqlite3_errmsg(impl_->db));
    1008                 :            : 
    1009                 :            :     free_err_msg();
    1010                 :            :     finalize_stmt(header_stmt);
    1011                 :            :     close_db();
    1012                 :            : 
    1013                 :            :     return;
    1014                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1015                 :            :   }
    1016                 :            : 
    1017                 :            :   // get date
    1018         [ +  - ]:        151 :   const std::string& date_str = get_format_date(&impl_->time_current);
    1019                 :            : 
    1020                 :            :   // insert header table
    1021         [ +  - ]:        151 :   ::sqlite3_bind_int(header_stmt, get_column(0), VLINK_VERSION_MAJOR);
    1022         [ +  - ]:        151 :   ::sqlite3_bind_int(header_stmt, get_column(1), VLINK_VERSION_MINOR);
    1023         [ +  - ]:        151 :   ::sqlite3_bind_int(header_stmt, get_column(2), VLINK_VERSION_PATCH);
    1024         [ +  - ]:        151 :   ::sqlite3_bind_int64(header_stmt, get_column(3), 0);
    1025         [ +  - ]:        151 :   ::sqlite3_bind_int64(header_stmt, get_column(4), 0);
    1026         [ +  - ]:        151 :   ::sqlite3_bind_text(header_stmt, get_column(5), "MicroSecond", -1, SQLITE_STATIC);
    1027   [ +  +  +  - ]:        151 :   ::sqlite3_bind_text(header_stmt, get_column(6), impl_->enable_compressed ? "lzav" : "None", -1, SQLITE_STATIC);
    1028         [ +  - ]:        151 :   ::sqlite3_bind_text(header_stmt, get_column(7), impl_->app_name.data(), impl_->app_name.size(), SQLITE_STATIC);
    1029         [ +  - ]:        151 :   ::sqlite3_bind_text(header_stmt, get_column(8), date_str.data(), date_str.size(), SQLITE_STATIC);
    1030         [ +  - ]:        151 :   ::sqlite3_bind_text(header_stmt, get_column(9), impl_->tag_name.data(), impl_->tag_name.size(), SQLITE_STATIC);
    1031         [ +  - ]:        151 :   ::sqlite3_bind_int(header_stmt, get_column(10), 0);
    1032         [ +  - ]:        151 :   ::sqlite3_bind_int(header_stmt, get_column(11), impl_->timezone_diff);
    1033         [ +  - ]:        151 :   ::sqlite3_bind_int64(header_stmt, get_column(12), impl_->start_timestamp);
    1034                 :            : 
    1035         [ +  - ]:        151 :   ret = ::sqlite3_step(header_stmt);
    1036                 :            : 
    1037         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_DONE) {
    1038                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
    1039                 :            :     CLOG_F("Failed to insert header table: %s.", ::sqlite3_errmsg(impl_->db));
    1040                 :            : 
    1041                 :            :     free_err_msg();
    1042                 :            :     finalize_stmt(header_stmt);
    1043                 :            :     close_db();
    1044                 :            : 
    1045                 :            :     return;
    1046                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1047                 :            :   }
    1048                 :            : 
    1049                 :        151 :   finalize_stmt(header_stmt);
    1050                 :            : 
    1051                 :            :   // create schema table
    1052         [ +  - ]:        151 :   ret = ::sqlite3_exec(impl_->db, "CREATE TABLE IF NOT EXISTS VLinkSchemas(ser TEXT, encoding TEXT, data BLOB);",
    1053                 :            :                        nullptr, nullptr, &err_msg);
    1054                 :            : 
    1055         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
    1056                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
    1057                 :            :     CLOG_F("Failed to create schema table: %s.", err_msg);
    1058                 :            : 
    1059                 :            :     free_err_msg();
    1060                 :            :     close_db();
    1061                 :            : 
    1062                 :            :     return;
    1063                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1064                 :            :   }
    1065                 :            : 
    1066                 :            :   // prepare schema table
    1067         [ +  - ]:        151 :   ret = ::sqlite3_prepare_v2(impl_->db, "INSERT INTO VLinkSchemas(ser, encoding, data) VALUES (?, ?, ?);", -1,
    1068                 :        151 :                              &impl_->schemas_stmt, nullptr);
    1069                 :            : 
    1070         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
    1071                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
    1072                 :            :     CLOG_F("Failed to prepare schema table: %s.", ::sqlite3_errmsg(impl_->db));
    1073                 :            : 
    1074                 :            :     free_err_msg();
    1075                 :            :     close_db();
    1076                 :            : 
    1077                 :            :     return;
    1078                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1079                 :            :   }
    1080                 :            : 
    1081                 :            :   // create url table
    1082         [ +  - ]:        151 :   ret = ::sqlite3_exec(impl_->db,
    1083                 :            :                        "CREATE TABLE IF NOT EXISTS VLinkUrls(id INTEGER, url TEXT, type TEXT, ser TEXT, encoding TEXT, "
    1084                 :            :                        "count INTEGER, loss REAL, size INTEGER, freq REAL);",
    1085                 :            :                        nullptr, nullptr, &err_msg);
    1086                 :            : 
    1087         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
    1088                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
    1089                 :            :     CLOG_F("Failed to create urls table: %s.", err_msg);
    1090                 :            : 
    1091                 :            :     free_err_msg();
    1092                 :            :     close_db();
    1093                 :            : 
    1094                 :            :     return;
    1095                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1096                 :            :   }
    1097                 :            : 
    1098                 :            :   // prepare url table
    1099         [ +  - ]:        151 :   ret = ::sqlite3_prepare_v2(impl_->db,
    1100                 :            :                              "INSERT INTO VLinkUrls (id, url, type, ser, encoding, count, loss, size, freq) VALUES "
    1101                 :            :                              "(?, ?, ?, ?, ?, ?, ?, ?, ?);",
    1102                 :        151 :                              -1, &impl_->urls_stmt, nullptr);
    1103                 :            : 
    1104         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
    1105                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
    1106                 :            :     CLOG_F("Failed to prepare urls table: %s.", ::sqlite3_errmsg(impl_->db));
    1107                 :            : 
    1108                 :            :     free_err_msg();
    1109                 :            :     close_db();
    1110                 :            : 
    1111                 :            :     return;
    1112                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1113                 :            :   }
    1114                 :            : 
    1115                 :            :   // create data table
    1116         [ +  - ]:        151 :   ret = ::sqlite3_exec(impl_->db,
    1117                 :            :                        "CREATE TABLE IF NOT EXISTS VLinkDatas(elapsed INTEGER, url INTEGER, action TEXT, data BLOB);",
    1118                 :            :                        nullptr, nullptr, &err_msg);
    1119                 :            : 
    1120         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
    1121                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
    1122                 :            :     CLOG_F("Failed to create datas table: %s.", err_msg);
    1123                 :            : 
    1124                 :            :     free_err_msg();
    1125                 :            :     close_db();
    1126                 :            : 
    1127                 :            :     return;
    1128                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1129                 :            :   }
    1130                 :            : 
    1131                 :            :   // prepare data table
    1132         [ +  - ]:        151 :   ret = ::sqlite3_prepare_v2(impl_->db, "INSERT INTO VLinkDatas (elapsed, url, action, data) VALUES (?, ?, ?, ?);", -1,
    1133                 :        151 :                              &impl_->datas_stmt, nullptr);
    1134                 :            : 
    1135         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
    1136                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
    1137                 :            :     CLOG_F("Failed to prepare datas table: %s.", ::sqlite3_errmsg(impl_->db));
    1138                 :            : 
    1139                 :            :     free_err_msg();
    1140                 :            :     close_db();
    1141                 :            : 
    1142                 :            :     return;
    1143                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1144                 :            :   }
    1145                 :            : 
    1146                 :            :   // prepare update_header table
    1147         [ +  - ]:        151 :   ret = sqlite3_prepare_v2(impl_->db, "UPDATE VLinkHeader SET count=?, duration=? WHERE ROWID=1;", -1,
    1148                 :        151 :                            &impl_->update_header_stmt, nullptr);
    1149                 :            : 
    1150         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
    1151                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
    1152                 :            :     CLOG_F("Failed to prepare update_header: %s.", ::sqlite3_errmsg(impl_->db));
    1153                 :            : 
    1154                 :            :     free_err_msg();
    1155                 :            :     close_db();
    1156                 :            : 
    1157                 :            :     return;
    1158                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1159                 :            :   }
    1160                 :            : 
    1161                 :            :   // prepare update_url table
    1162         [ +  - ]:        151 :   ret = sqlite3_prepare_v2(impl_->db, "UPDATE VLinkUrls SET count=?, size=?, freq=? WHERE url=?;", -1,
    1163                 :        151 :                            &impl_->update_urls_stmt, nullptr);
    1164                 :            : 
    1165         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
    1166                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
    1167                 :            :     CLOG_F("Failed to prepare update_urls: %s.", ::sqlite3_errmsg(impl_->db));
    1168                 :            : 
    1169                 :            :     free_err_msg();
    1170                 :            :     close_db();
    1171                 :            : 
    1172                 :            :     return;
    1173                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1174                 :            :   }
    1175                 :            : 
    1176                 :            :   // create idx_elapsed_url
    1177         [ +  - ]:        151 :   ret = ::sqlite3_exec(impl_->db, "CREATE INDEX IF NOT EXISTS idx_elapsed_url ON VLinkDatas(elapsed, url);", nullptr,
    1178                 :            :                        nullptr, &err_msg);
    1179                 :            : 
    1180         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
    1181                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
    1182                 :            :     CLOG_F("Failed to create datas idx_elapsed_url: %s.", err_msg);
    1183                 :            : 
    1184                 :            :     free_err_msg();
    1185                 :            :     close_db();
    1186                 :            : 
    1187                 :            :     return;
    1188                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1189                 :            :   }
    1190                 :            : 
    1191         [ +  - ]:        151 :   ret = sqlite3_prepare_v2(impl_->db, "UPDATE VLinkUrls SET ser=?, encoding=? WHERE url=?;", -1,
    1192                 :        151 :                            &impl_->update_url_meta_stmt, nullptr);
    1193                 :            : 
    1194         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
    1195                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
    1196                 :            :     CLOG_F("Failed to prepare update_url_meta: %s.", ::sqlite3_errmsg(impl_->db));
    1197                 :            : 
    1198                 :            :     free_err_msg();
    1199                 :            :     close_db();
    1200                 :            : 
    1201                 :            :     return;
    1202                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1203                 :            :   }
    1204                 :            : 
    1205         [ +  - ]:        151 :   ret = sqlite3_prepare_v2(impl_->db, "UPDATE VLinkUrls SET loss=? WHERE url=?;", -1, &impl_->update_url_loss_stmt,
    1206                 :            :                            nullptr);
    1207                 :            : 
    1208         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
    1209                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
    1210                 :            :     CLOG_F("Failed to prepare update_url_loss: %s.", ::sqlite3_errmsg(impl_->db));
    1211                 :            : 
    1212                 :            :     free_err_msg();
    1213                 :            :     close_db();
    1214                 :            : 
    1215                 :            :     return;
    1216                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1217                 :            :   }
    1218                 :            : 
    1219         [ +  - ]:        151 :   ret = sqlite3_prepare_v2(impl_->db, "UPDATE VLinkHeader SET complete=1 WHERE ROWID=1;", -1,
    1220                 :        151 :                            &impl_->update_complete_stmt, nullptr);
    1221                 :            : 
    1222         [ -  + ]:        151 :   if VUNLIKELY (ret != SQLITE_OK) {
    1223                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
    1224                 :            :     CLOG_F("Failed to prepare update_complete: %s.", ::sqlite3_errmsg(impl_->db));
    1225                 :            : 
    1226                 :            :     free_err_msg();
    1227                 :            :     close_db();
    1228                 :            : 
    1229                 :            :     return;
    1230                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1231                 :            :   }
    1232                 :            : 
    1233                 :        151 :   free_err_msg();
    1234                 :            : 
    1235         [ +  - ]:        151 :   sync_cache();
    1236                 :            : 
    1237                 :            : #else
    1238                 :            :   (void)path;
    1239                 :            :   VLOG_F("VDBWriter: The compile macro VLINK_ENABLE_SQLITE is not turned on.");
    1240                 :            : #endif
    1241                 :            : 
    1242                 :        151 :   impl_->last_timestamp = 0;
    1243         [ +  - ]:        151 : }
    1244                 :            : 
    1245                 :         62 : void VDBWriter::open_split(const std::string& path) {
    1246                 :            : #ifdef _WIN32
    1247                 :            :   const auto file_name = std::filesystem::path(Helpers::string_to_wstring(path)).filename();
    1248                 :            :   open(Helpers::path_to_string(impl_->split_output_dir / file_name));
    1249                 :            : #else
    1250   [ +  -  +  -  :         62 :   open((impl_->split_output_dir / std::filesystem::path(path).filename()).string());
             +  -  +  - ]
    1251                 :            : #endif
    1252                 :         62 : }
    1253                 :            : 
    1254                 :        171 : void VDBWriter::close_segment() {
    1255                 :            : #ifdef VLINK_ENABLE_SQLITE
    1256                 :            : 
    1257         [ +  + ]:        171 :   if (!impl_->db) {
    1258                 :         20 :     return;
    1259                 :            :   }
    1260                 :            : 
    1261                 :        151 :   bool close_success = sync_cache();
    1262                 :            : 
    1263         [ -  + ]:        151 :   if (!close_success) {
    1264                 :            :     if (impl_->in_cached.load(std::memory_order_relaxed)) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1265                 :            :       rollback_cache();                                      // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1266                 :            :     }
    1267                 :            :   } else {
    1268                 :        151 :     close_success = begin_cache();
    1269                 :            :   }
    1270                 :            : 
    1271         [ +  - ]:        151 :   if (close_success) {
    1272                 :        151 :     int ret = SQLITE_OK;
    1273                 :            : 
    1274         [ +  + ]:        359 :     for (const auto& [url, info] : impl_->url_map) {
    1275         [ +  - ]:        208 :       ::sqlite3_bind_int64(impl_->update_urls_stmt, get_column(0), info.count);
    1276         [ +  - ]:        208 :       ::sqlite3_bind_int64(impl_->update_urls_stmt, get_column(1), info.size);
    1277         [ +  - ]:        208 :       ::sqlite3_bind_double(impl_->update_urls_stmt, get_column(2), info.freq);
    1278         [ +  - ]:        208 :       ::sqlite3_bind_text(impl_->update_urls_stmt, get_column(3), url.c_str(), url.size(), SQLITE_STATIC);
    1279                 :            : 
    1280         [ +  - ]:        208 :       ret = ::sqlite3_step(impl_->update_urls_stmt);
    1281                 :            : 
    1282         [ -  + ]:        208 :       if VUNLIKELY (ret != SQLITE_DONE) {
    1283                 :            :         CLOG_W("Failed to update urls table: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1284                 :            :         close_success = false;                                                    // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1285                 :            :       }
    1286                 :            : 
    1287         [ +  - ]:        208 :       ret = ::sqlite3_reset(impl_->update_urls_stmt);
    1288                 :            : 
    1289         [ -  + ]:        208 :       if VUNLIKELY (ret != SQLITE_OK) {
    1290                 :            :         CLOG_W("Failed to reset urls table: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1291                 :            :         close_success = false;                                                   // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1292                 :            :       }
    1293                 :            : 
    1294         [ -  + ]:        208 :       if VUNLIKELY (!close_success) {
    1295                 :            :         break;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1296                 :            :       }
    1297                 :            :     }
    1298                 :            : 
    1299         [ +  - ]:        151 :     if (close_success) {
    1300                 :        151 :       ::sqlite3_bind_int64(impl_->update_header_stmt, get_column(0), impl_->current_row);
    1301                 :        151 :       ::sqlite3_bind_int64(impl_->update_header_stmt, get_column(1), impl_->last_timestamp);
    1302                 :            : 
    1303                 :        151 :       ret = ::sqlite3_step(impl_->update_header_stmt);
    1304                 :            : 
    1305         [ -  + ]:        151 :       if VUNLIKELY (ret != SQLITE_DONE) {
    1306                 :            :         CLOG_W("Failed to update header table: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1307                 :            :         close_success = false;                                                      // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1308                 :            :       }
    1309                 :            : 
    1310                 :        151 :       ret = ::sqlite3_reset(impl_->update_header_stmt);
    1311                 :            : 
    1312         [ -  + ]:        151 :       if VUNLIKELY (ret != SQLITE_OK) {
    1313                 :            :         CLOG_W("Failed to reset header table: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1314                 :            :         close_success = false;                                                     // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1315                 :            :       }
    1316                 :            :     }
    1317                 :            : 
    1318         [ +  - ]:        151 :     if (close_success) {
    1319   [ +  -  +  - ]:        151 :       std::lock_guard lock(sample_mutex());
    1320                 :            : 
    1321   [ +  -  +  + ]:        190 :       for (const auto& [url, loss] : url_loss_map_ref()) {
    1322   [ +  -  +  + ]:         78 :         for (const auto& recorded_url : recorded_urls_for_origin(url)) {
    1323         [ +  - ]:         39 :           ::sqlite3_bind_double(impl_->update_url_loss_stmt, get_column(0), loss);
    1324         [ +  - ]:         39 :           ::sqlite3_bind_text(impl_->update_url_loss_stmt, get_column(1), recorded_url.c_str(), recorded_url.size(),
    1325                 :            :                               SQLITE_STATIC);
    1326                 :            : 
    1327         [ +  - ]:         39 :           ret = ::sqlite3_step(impl_->update_url_loss_stmt);
    1328                 :            : 
    1329         [ -  + ]:         39 :           if VUNLIKELY (ret != SQLITE_DONE) {
    1330                 :            :             CLOG_W("Failed to update url loss: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1331                 :            :             close_success = false;                                                  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1332                 :            :           }
    1333                 :            : 
    1334         [ +  - ]:         39 :           ret = ::sqlite3_reset(impl_->update_url_loss_stmt);
    1335                 :            : 
    1336         [ -  + ]:         39 :           if VUNLIKELY (ret != SQLITE_OK) {
    1337                 :            :             CLOG_W("Failed to reset url loss: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1338                 :            :             close_success = false;                                                 // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1339                 :            :           }
    1340                 :            : 
    1341         [ -  + ]:         39 :           if VUNLIKELY (!close_success) {
    1342                 :            :             break;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1343                 :            :           }
    1344                 :         39 :         }
    1345                 :            : 
    1346         [ -  + ]:         39 :         if VUNLIKELY (!close_success) {
    1347                 :            :           break;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1348                 :            :         }
    1349                 :            :       }
    1350                 :        151 :     }
    1351                 :            : 
    1352         [ +  - ]:        151 :     if (close_success) {
    1353                 :        151 :       ret = ::sqlite3_step(impl_->update_complete_stmt);
    1354                 :            : 
    1355         [ -  + ]:        151 :       if VUNLIKELY (ret != SQLITE_DONE) {
    1356                 :            :         CLOG_W("Failed to mark database complete: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1357                 :            :         close_success = false;                                                         // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1358                 :            :       }
    1359                 :            : 
    1360                 :        151 :       ret = ::sqlite3_reset(impl_->update_complete_stmt);
    1361                 :            : 
    1362         [ -  + ]:        151 :       if VUNLIKELY (ret != SQLITE_OK) {
    1363                 :            :         CLOG_W("Failed to reset complete flag: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1364                 :            :         close_success = false;                                                      // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1365                 :            :       }
    1366                 :            :     }
    1367                 :            : 
    1368         [ +  - ]:        151 :     if (close_success) {
    1369                 :        151 :       close_success = sync_cache();
    1370                 :            :     }
    1371                 :            :   }
    1372                 :            : 
    1373         [ -  + ]:        151 :   if VUNLIKELY (!close_success) {
    1374                 :          0 :     set_fail();
    1375                 :            : 
    1376         [ #  # ]:          0 :     if (impl_->in_cached.load(std::memory_order_relaxed)) {
    1377                 :            :       rollback_cache();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1378                 :            :     }
    1379                 :            :   }
    1380                 :            : 
    1381         [ +  - ]:        151 :   if VLIKELY (impl_->datas_stmt) {
    1382                 :        151 :     ::sqlite3_finalize(impl_->datas_stmt);
    1383                 :        151 :     impl_->datas_stmt = nullptr;
    1384                 :            :   }
    1385                 :            : 
    1386         [ +  - ]:        151 :   if VLIKELY (impl_->urls_stmt) {
    1387                 :        151 :     ::sqlite3_finalize(impl_->urls_stmt);
    1388                 :        151 :     impl_->urls_stmt = nullptr;
    1389                 :            :   }
    1390                 :            : 
    1391         [ +  - ]:        151 :   if VLIKELY (impl_->schemas_stmt) {
    1392                 :        151 :     ::sqlite3_finalize(impl_->schemas_stmt);
    1393                 :        151 :     impl_->schemas_stmt = nullptr;
    1394                 :            :   }
    1395                 :            : 
    1396         [ +  - ]:        151 :   if VLIKELY (impl_->update_urls_stmt) {
    1397                 :        151 :     ::sqlite3_finalize(impl_->update_urls_stmt);
    1398                 :        151 :     impl_->update_urls_stmt = nullptr;
    1399                 :            :   }
    1400                 :            : 
    1401         [ +  - ]:        151 :   if VLIKELY (impl_->update_url_meta_stmt) {
    1402                 :        151 :     ::sqlite3_finalize(impl_->update_url_meta_stmt);
    1403                 :        151 :     impl_->update_url_meta_stmt = nullptr;
    1404                 :            :   }
    1405                 :            : 
    1406         [ +  - ]:        151 :   if VLIKELY (impl_->update_url_loss_stmt) {
    1407                 :        151 :     ::sqlite3_finalize(impl_->update_url_loss_stmt);
    1408                 :        151 :     impl_->update_url_loss_stmt = nullptr;
    1409                 :            :   }
    1410                 :            : 
    1411         [ +  - ]:        151 :   if VLIKELY (impl_->update_header_stmt) {
    1412                 :        151 :     ::sqlite3_finalize(impl_->update_header_stmt);
    1413                 :        151 :     impl_->update_header_stmt = nullptr;
    1414                 :            :   }
    1415                 :            : 
    1416         [ +  - ]:        151 :   if VLIKELY (impl_->update_complete_stmt) {
    1417                 :        151 :     ::sqlite3_finalize(impl_->update_complete_stmt);
    1418                 :        151 :     impl_->update_complete_stmt = nullptr;
    1419                 :            :   }
    1420                 :            : 
    1421         [ +  + ]:        151 :   if (impl_->config.wal_mode) {
    1422                 :          2 :     ::sqlite3_exec(impl_->db, "PRAGMA journal_mode = OFF;", nullptr, nullptr, nullptr);
    1423                 :            :   }
    1424                 :            : 
    1425         [ +  + ]:        151 :   if (impl_->config.optimize_on_exit) {
    1426                 :         18 :     ::sqlite3_exec(impl_->db, "PRAGMA optimize;", nullptr, nullptr, nullptr);
    1427                 :            :   }
    1428                 :            : 
    1429         [ +  - ]:        151 :   if VLIKELY (impl_->db) {
    1430         [ +  - ]:        151 :     const char* err_ptr = ::sqlite3_errmsg(impl_->db);
    1431   [ +  -  +  -  :        151 :     std::string close_err = err_ptr ? err_ptr : std::string{};
             +  -  -  - ]
    1432         [ +  - ]:        151 :     int ret = ::sqlite3_close_v2(impl_->db);
    1433                 :            : 
    1434         [ -  + ]:        151 :     if VUNLIKELY (ret != SQLITE_OK) {
    1435                 :            :       CLOG_W("Failed to close database (rc=%d): %s.", ret, close_err.c_str());  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1436                 :            :       set_fail();                                                               // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1437                 :            :     }
    1438                 :            : 
    1439                 :        151 :     impl_->db = nullptr;
    1440                 :        151 :   }
    1441                 :            : 
    1442                 :        151 :   impl_->url_map.clear();
    1443                 :        151 :   impl_->ser_map.clear();
    1444                 :        151 :   url_loss_map_ref().clear();
    1445                 :            : 
    1446                 :        151 :   impl_->current_row = 0;
    1447                 :        151 :   impl_->current_size = 0;
    1448                 :        151 :   impl_->has_oversize = false;
    1449                 :            : 
    1450                 :        151 :   impl_->in_cached.store(false, std::memory_order_relaxed);
    1451                 :        151 :   impl_->cached_size.store(0, std::memory_order_relaxed);
    1452                 :        151 :   impl_->cache_snapshot.reset();
    1453                 :            : #endif
    1454                 :            : 
    1455                 :        151 :   impl_->last_timestamp = 0;
    1456                 :            : }
    1457                 :            : 
    1458                 :        347 : bool VDBWriter::write(const std::string& url, const std::string& ser_type, SchemaType schema_type,
    1459                 :            :                       ActionType action_type, const Bytes& data, int64_t microseconds_timestamp) {
    1460                 :            : #ifdef VLINK_ENABLE_SQLITE
    1461                 :            : 
    1462         [ -  + ]:        347 :   if VUNLIKELY (!impl_->db) {
    1463                 :            :     VLOG_W("VDBWriter: Sqlite not open.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1464                 :            :     return false;                           // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1465                 :            :   }
    1466                 :            : 
    1467   [ +  +  +  +  :        347 :   if VUNLIKELY (impl_->is_split_mode.load(std::memory_order_acquire) && !impl_->split_first) {
                   +  + ]
    1468                 :         32 :     impl_->split_first = true;
    1469                 :            : 
    1470         [ +  - ]:         32 :     std::lock_guard split_lock(impl_->split_mtx);
    1471                 :            : 
    1472   [ +  +  +  +  :         35 :     if (!impl_->split_before && impl_->split_callback && impl_->split_index.load(std::memory_order_relaxed) == 0) {
             +  -  +  + ]
    1473         [ +  - ]:          3 :       impl_->split_callback(0, impl_->split_filename);
    1474                 :            :     }
    1475                 :         32 :   }
    1476                 :            : 
    1477                 :        347 :   int ret = 0;
    1478                 :            : 
    1479                 :        347 :   bool do_compress = false;
    1480                 :        347 :   Bytes compressed_data;
    1481                 :        347 :   bool do_split = false;
    1482                 :            : 
    1483   [ +  -  -  + ]:        347 :   if VUNLIKELY (!begin_cache()) {
    1484                 :            :     return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1485                 :            :   }
    1486                 :            : 
    1487                 :            :   // delete data, when limit
    1488   [ +  +  +  +  :        355 :   while (impl_->current_row > impl_->config.max_row_count || impl_->current_size > impl_->config.max_bytes_size) {
                   +  + ]
    1489         [ +  + ]:         10 :     if VUNLIKELY (!impl_->has_oversize) {
    1490         [ +  + ]:          8 :       if (impl_->config.enable_limit) {
    1491   [ +  -  +  - ]:         12 :         VLOG_W("VDBWriter: The number of messages has reached the upper limit, the oldest data will be deleted.");
    1492                 :            :       } else {
    1493   [ +  -  +  - ]:          4 :         VLOG_W("VDBWriter: The number of messages has reached the upper limit, data after that will be ignored.");
    1494                 :            :       }
    1495                 :            : 
    1496                 :          8 :       impl_->has_oversize = true;
    1497                 :            :     }
    1498                 :            : 
    1499         [ +  + ]:         10 :     if (!impl_->config.enable_limit) {
    1500         [ +  - ]:          2 :       rollback_cache();
    1501                 :          2 :       return false;
    1502                 :            :     }
    1503                 :            : 
    1504                 :          8 :     ::sqlite3_stmt* select_stmt = nullptr;
    1505                 :          8 :     ::sqlite3_stmt* delete_row_stmt = nullptr;
    1506         [ +  - ]:          8 :     ret = ::sqlite3_prepare_v2(impl_->db, "SELECT rowid, url, length(data) FROM VLinkDatas ORDER BY rowid LIMIT 1;", -1,
    1507                 :            :                                &select_stmt, nullptr);
    1508                 :            : 
    1509         [ +  - ]:          8 :     if VLIKELY (ret == SQLITE_OK) {
    1510         [ +  - ]:          8 :       ret = ::sqlite3_step(select_stmt);
    1511                 :            : 
    1512         [ +  - ]:          8 :       if VLIKELY (ret == SQLITE_ROW) {
    1513         [ +  - ]:          8 :         const auto erase_row_id = ::sqlite3_column_int64(select_stmt, 0);
    1514         [ +  - ]:          8 :         const auto erase_url_id = ::sqlite3_column_int(select_stmt, 1);
    1515         [ +  - ]:          8 :         const auto erase_blob_size = ::sqlite3_column_int64(select_stmt, 2);
    1516                 :          8 :         auto erase_size = static_cast<size_t>(erase_blob_size);
    1517                 :            : 
    1518   [ +  +  +  -  :          8 :         if (impl_->enable_compressed && erase_blob_size >= 13) {
                   +  + ]
    1519                 :          6 :           std::array<uint8_t, 13> erase_data{};
    1520                 :          6 :           ::sqlite3_blob* erase_blob = nullptr;
    1521         [ +  - ]:          6 :           ret = ::sqlite3_blob_open(impl_->db, "main", "VLinkDatas", "data", erase_row_id, 0, &erase_blob);
    1522                 :            : 
    1523         [ +  - ]:          6 :           if VLIKELY (ret == SQLITE_OK) {
    1524         [ +  - ]:          6 :             ret = ::sqlite3_blob_read(erase_blob, erase_data.data(), 9, 0);
    1525                 :            :           }
    1526                 :            : 
    1527         [ +  - ]:          6 :           if VLIKELY (ret == SQLITE_OK) {
    1528         [ +  - ]:          6 :             ret = ::sqlite3_blob_read(erase_blob, erase_data.data() + 9, 4, static_cast<int>(erase_blob_size) - 4);
    1529                 :            :           }
    1530                 :            : 
    1531         [ +  - ]:          6 :           if (erase_blob) {
    1532         [ +  - ]:          6 :             const auto close_ret = ::sqlite3_blob_close(erase_blob);
    1533         [ +  - ]:          6 :             if (ret == SQLITE_OK) {
    1534                 :          6 :               ret = close_ret;
    1535                 :            :             }
    1536                 :            :           }
    1537                 :            : 
    1538         [ -  + ]:          6 :           if VUNLIKELY (ret != SQLITE_OK) {
    1539   [ #  #  #  #  :          0 :             CLOG_W("VDBWriter: Failed to read compressed data metadata while evicting: %s.",
                   #  # ]
    1540                 :            :                    ::sqlite3_errmsg(impl_->db));
    1541         [ #  # ]:          0 :             ::sqlite3_finalize(select_stmt);
    1542         [ #  # ]:          0 :             rollback_cache();
    1543                 :          0 :             return false;
    1544                 :            :           }
    1545                 :            : 
    1546         [ +  - ]:          6 :           if (Bytes::is_compress_data(erase_data.data(), erase_data.size())) {
    1547                 :          6 :             erase_size = (static_cast<size_t>(erase_data[4]) << 24U) | (static_cast<size_t>(erase_data[5]) << 16U) |
    1548                 :          6 :                          (static_cast<size_t>(erase_data[6]) << 8U) | static_cast<size_t>(erase_data[7]);
    1549                 :            :           }
    1550                 :            :         }
    1551                 :            : 
    1552                 :          8 :         auto total_url_iter = impl_->total_url_map.end();
    1553                 :            : 
    1554   [ +  -  +  -  :          8 :         if VLIKELY (erase_url_id >= 0 && static_cast<size_t>(erase_url_id) < impl_->total_url_list.size()) {
                   +  - ]
    1555         [ +  - ]:          8 :           total_url_iter = impl_->total_url_map.find(impl_->total_url_list[static_cast<size_t>(erase_url_id)]);
    1556                 :            :         }
    1557                 :            : 
    1558         [ -  + ]:          8 :         if VUNLIKELY (total_url_iter == impl_->total_url_map.end()) {
    1559   [ #  #  #  # ]:          0 :           CLOG_W("VDBWriter: URL metadata not found while evicting the oldest data.");
    1560         [ #  # ]:          0 :           ::sqlite3_finalize(select_stmt);
    1561         [ #  # ]:          0 :           rollback_cache();
    1562                 :          0 :           return false;
    1563                 :            :         }
    1564                 :            : 
    1565         [ +  - ]:          8 :         auto url_iter = impl_->url_map.find(total_url_iter->first);
    1566                 :            : 
    1567         [ -  + ]:          8 :         if VUNLIKELY (url_iter == impl_->url_map.end()) {
    1568   [ #  #  #  # ]:          0 :           CLOG_W("VDBWriter: Segment URL metadata not found while evicting the oldest data.");
    1569         [ #  # ]:          0 :           ::sqlite3_finalize(select_stmt);
    1570         [ #  # ]:          0 :           rollback_cache();
    1571                 :          0 :           return false;
    1572                 :            :         }
    1573                 :            : 
    1574         [ +  - ]:          8 :         ::sqlite3_finalize(select_stmt);
    1575                 :          8 :         select_stmt = nullptr;
    1576                 :            : 
    1577         [ +  - ]:          8 :         ret = ::sqlite3_prepare_v2(impl_->db, "DELETE FROM VLinkDatas WHERE rowid = ?;", -1, &delete_row_stmt, nullptr);
    1578                 :            : 
    1579         [ +  - ]:          8 :         if VLIKELY (ret == SQLITE_OK) {
    1580         [ +  - ]:          8 :           ::sqlite3_bind_int64(delete_row_stmt, get_column(0), erase_row_id);
    1581         [ +  - ]:          8 :           ret = ::sqlite3_step(delete_row_stmt);
    1582                 :            :         }
    1583                 :            : 
    1584         [ +  - ]:          8 :         if VLIKELY (ret == SQLITE_DONE) {
    1585                 :          8 :           --impl_->current_row;
    1586                 :          8 :           impl_->current_size -= static_cast<int64_t>(erase_size);
    1587                 :          8 :           --impl_->total_current_row;
    1588                 :          8 :           impl_->total_current_size -= static_cast<int64_t>(erase_size);
    1589                 :            : 
    1590                 :          8 :           auto& url_info = url_iter->second;
    1591                 :          8 :           auto& total_url_info = total_url_iter->second;
    1592                 :          8 :           --url_info.count;
    1593                 :          8 :           url_info.size -= erase_size;
    1594                 :          8 :           --total_url_info.count;
    1595                 :          8 :           total_url_info.size -= erase_size;
    1596                 :            : 
    1597         [ +  + ]:          8 :           if (url_info.count == 0) {
    1598                 :          6 :             url_info.first_timestamp = -1;
    1599                 :          6 :             url_info.last_timestamp = -1;
    1600                 :          6 :             url_info.freq = 0;
    1601         [ +  - ]:          2 :           } else if (url_info.first_timestamp >= 0) {
    1602         [ -  + ]:          2 :             if (url_info.count == 1) {
    1603                 :          0 :               url_info.first_timestamp = url_info.last_timestamp;
    1604                 :          0 :               url_info.freq = 0;
    1605                 :            :             } else {
    1606                 :          2 :               ::sqlite3_stmt* timestamp_stmt = nullptr;
    1607         [ +  - ]:          2 :               ret = ::sqlite3_prepare_v2(
    1608                 :          2 :                   impl_->db, "SELECT elapsed FROM VLinkDatas WHERE rowid > ? AND url = ? ORDER BY rowid LIMIT 1;", -1,
    1609                 :            :                   &timestamp_stmt, nullptr);
    1610                 :            : 
    1611         [ +  - ]:          2 :               if VLIKELY (ret == SQLITE_OK) {
    1612         [ +  - ]:          2 :                 ::sqlite3_bind_int64(timestamp_stmt, get_column(0), erase_row_id);
    1613         [ +  - ]:          2 :                 ::sqlite3_bind_int(timestamp_stmt, get_column(1), erase_url_id);
    1614         [ +  - ]:          2 :                 ret = ::sqlite3_step(timestamp_stmt);
    1615                 :            :               }
    1616                 :            : 
    1617         [ -  + ]:          2 :               if VUNLIKELY (ret != SQLITE_ROW) {
    1618   [ #  #  #  #  :          0 :                 CLOG_W("VDBWriter: Failed to refresh URL metadata after eviction: %s.", ::sqlite3_errmsg(impl_->db));
                   #  # ]
    1619         [ #  # ]:          0 :                 if (timestamp_stmt) {
    1620         [ #  # ]:          0 :                   ::sqlite3_finalize(timestamp_stmt);
    1621                 :            :                 }
    1622         [ #  # ]:          0 :                 ::sqlite3_finalize(delete_row_stmt);
    1623         [ #  # ]:          0 :                 rollback_cache();
    1624                 :          0 :                 return false;
    1625                 :            :               }
    1626                 :            : 
    1627         [ +  - ]:          2 :               url_info.first_timestamp = ::sqlite3_column_int64(timestamp_stmt, 0);
    1628         [ +  - ]:          2 :               ::sqlite3_finalize(timestamp_stmt);
    1629                 :            : 
    1630                 :          2 :               const auto duration = (url_info.last_timestamp - url_info.first_timestamp) / 1000'000.0;
    1631         [ +  - ]:          2 :               url_info.freq = duration > 0 ? url_info.count / duration : 0;
    1632                 :            :             }
    1633                 :            :           }
    1634                 :            : 
    1635         [ +  + ]:          8 :           if (total_url_info.count == 0) {
    1636                 :          6 :             total_url_info.first_timestamp = -1;
    1637                 :          6 :             total_url_info.last_timestamp = -1;
    1638                 :          6 :             total_url_info.freq = 0;
    1639         [ +  - ]:          2 :           } else if (total_url_info.count == url_info.count) {
    1640                 :          2 :             total_url_info.first_timestamp = url_info.first_timestamp;
    1641                 :          2 :             total_url_info.last_timestamp = url_info.last_timestamp;
    1642                 :          2 :             total_url_info.freq = url_info.freq;
    1643         [ #  # ]:          0 :           } else if (total_url_info.first_timestamp >= 0) {
    1644         [ #  # ]:          0 :             if (url_info.count > 0) {
    1645                 :          0 :               total_url_info.last_timestamp = url_info.last_timestamp;
    1646                 :            :             } else {
    1647                 :          0 :               total_url_info.last_timestamp = total_url_info.previous_segment_last_timestamp;
    1648                 :            :             }
    1649                 :            : 
    1650                 :          0 :             const auto duration = (total_url_info.last_timestamp - total_url_info.first_timestamp) / 1000'000.0;
    1651         [ #  # ]:          0 :             total_url_info.freq = duration > 0 ? total_url_info.count / duration : 0;
    1652                 :            :           }
    1653                 :            :         } else {
    1654                 :            :           // LCOV_EXCL_START GCOVR_EXCL_START
    1655                 :            :           CLOG_W("Failed to erase datas table: %s.", ::sqlite3_errmsg(impl_->db));
    1656                 :            :           if VLIKELY (delete_row_stmt) {
    1657                 :            :             ::sqlite3_finalize(delete_row_stmt);
    1658                 :            :             delete_row_stmt = nullptr;
    1659                 :            :           }
    1660                 :            :           if VLIKELY (select_stmt) {
    1661                 :            :             ::sqlite3_finalize(select_stmt);
    1662                 :            :             select_stmt = nullptr;
    1663                 :            :           }
    1664                 :            :           rollback_cache();
    1665                 :            :           return false;
    1666                 :            :           // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1667                 :            :         }
    1668                 :            :       } else {
    1669         [ #  # ]:          0 :         if (ret == SQLITE_DONE) {
    1670   [ #  #  #  # ]:          0 :           CLOG_W("VDBWriter: No data available while enforcing the configured limit.");
    1671                 :            :         } else {
    1672   [ #  #  #  #  :          0 :           CLOG_W("VDBWriter: Failed to select data while enforcing the configured limit: %s.",
                   #  # ]
    1673                 :            :                  ::sqlite3_errmsg(impl_->db));
    1674                 :            :         }
    1675         [ #  # ]:          0 :         ::sqlite3_finalize(select_stmt);
    1676         [ #  # ]:          0 :         rollback_cache();
    1677                 :          0 :         return false;
    1678                 :            :       }
    1679                 :            : 
    1680         [ +  - ]:          8 :       if VLIKELY (delete_row_stmt) {
    1681         [ +  - ]:          8 :         ::sqlite3_finalize(delete_row_stmt);
    1682                 :          8 :         delete_row_stmt = nullptr;
    1683                 :            :       }
    1684                 :            : 
    1685         [ -  + ]:          8 :       if VLIKELY (select_stmt) {
    1686         [ #  # ]:          0 :         ::sqlite3_finalize(select_stmt);
    1687                 :          0 :         select_stmt = nullptr;
    1688                 :            :       }
    1689                 :            :     } else {
    1690                 :            :       CLOG_W("Failed to erase datas table: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1691                 :            :       rollback_cache();                                                         // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1692                 :            :       return false;                                                             // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1693                 :            :     }
    1694                 :            :   }
    1695                 :            : 
    1696                 :            :   // split
    1697                 :            : 
    1698   [ +  +  +  +  :        345 :   if (impl_->is_split_mode.load(std::memory_order_acquire) && !impl_->url_map.empty()) {
                   +  + ]
    1699   [ +  +  +  + ]:         38 :     if (impl_->config.split_by_time > 0 &&
    1700                 :          4 :         (microseconds_timestamp - impl_->config.begin_time * 1000) >
    1701         [ +  - ]:          4 :             impl_->config.split_by_time * 1000 * static_cast<int64_t>(impl_->split_file_list.size())) {
    1702                 :          4 :       do_split = true;
    1703   [ +  -  +  -  :         60 :     } else if (impl_->config.split_by_time <= 0 && impl_->config.split_by_size > 0 &&
                   +  + ]
    1704         [ +  + ]:         30 :                (impl_->current_size + static_cast<int64_t>(data.size())) > impl_->config.split_by_size) {
    1705                 :         25 :       do_split = true;
    1706                 :            :     } else {
    1707                 :            :       do_split = false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1708                 :            :     }
    1709                 :            : 
    1710         [ +  + ]:         34 :     if VUNLIKELY (do_split) {
    1711         [ +  - ]:         29 :       std::lock_guard split_lock(impl_->split_mtx);
    1712                 :            : 
    1713                 :         29 :       impl_->split_index.fetch_add(1, std::memory_order_relaxed);
    1714         [ +  - ]:         29 :       impl_->time_current = impl_->time_start + std::chrono::milliseconds(microseconds_timestamp / 1000U);
    1715                 :            : 
    1716         [ +  + ]:         29 :       if (impl_->config.split_name_by_time) {
    1717         [ +  + ]:          4 :         if (impl_->base_dir.empty()) {
    1718   [ +  -  +  - ]:          2 :           impl_->split_filename = get_format_date(&impl_->time_current, true) + ".vdb";
    1719                 :            :         } else {
    1720   [ +  -  +  -  :          2 :           impl_->split_filename = impl_->base_dir + "/" + get_format_date(&impl_->time_current, true) + ".vdb";
             +  -  +  - ]
    1721                 :            :         }
    1722                 :            :       } else {
    1723                 :         25 :         impl_->split_filename =
    1724   [ +  -  +  -  :         75 :             impl_->base_name + "." + std::to_string(impl_->split_index.load(std::memory_order_relaxed) + 1) + ".vdb";
             +  -  +  - ]
    1725                 :            :       }
    1726                 :            : 
    1727   [ +  +  +  -  :         29 :       if (impl_->split_before && impl_->split_callback) {
                   +  + ]
    1728         [ +  - ]:         46 :         impl_->split_callback(impl_->split_index.load(std::memory_order_relaxed), impl_->split_filename);
    1729                 :            :       }
    1730                 :            : 
    1731   [ +  -  -  + ]:         29 :       if VUNLIKELY (!sync_cache()) {
    1732                 :            :         impl_->split_index.fetch_sub(1, std::memory_order_relaxed);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1733                 :            :         return false;                                                // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1734                 :            :       }
    1735                 :            : 
    1736         [ +  - ]:         29 :       close_segment();
    1737                 :            : 
    1738   [ +  -  -  + ]:         29 :       if VUNLIKELY (!write_filex(false)) {
    1739                 :            :         set_fail();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1740                 :            :       }
    1741                 :            : 
    1742         [ +  - ]:         29 :       open_split(impl_->split_filename);
    1743                 :            : 
    1744   [ +  -  -  + ]:         29 :       if VUNLIKELY (!begin_cache()) {
    1745                 :            :         impl_->split_index.fetch_sub(1, std::memory_order_relaxed);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1746                 :            :         return false;                                                // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1747                 :            :       }
    1748                 :            : 
    1749   [ +  +  +  +  :         29 :       if (!impl_->split_before && impl_->split_callback) {
                   +  + ]
    1750         [ +  - ]:          6 :         impl_->split_callback(impl_->split_index.load(std::memory_order_relaxed), impl_->split_filename);
    1751                 :            :       }
    1752         [ +  - ]:         29 :     }
    1753                 :            :   }
    1754                 :            : 
    1755         [ +  - ]:        345 :   auto total_url_iter_ret = impl_->total_url_map.try_emplace(url, Impl::UrlMsgInfo());
    1756         [ +  - ]:        345 :   auto url_iter_ret = impl_->url_map.try_emplace(url, Impl::UrlMsgInfo());
    1757                 :            : 
    1758                 :         14 :   auto discard_new_url_entries = [this, &url_iter_ret, &total_url_iter_ret]() {
    1759         [ +  + ]:          5 :     if (url_iter_ret.second) {
    1760                 :          1 :       impl_->url_map.erase(url_iter_ret.first);
    1761                 :            :     }
    1762                 :            : 
    1763         [ +  + ]:          5 :     if (total_url_iter_ret.second) {
    1764                 :          1 :       impl_->total_url_map.erase(total_url_iter_ret.first);
    1765                 :            :     }
    1766                 :        350 :   };
    1767                 :            : 
    1768                 :        345 :   Impl::UrlMsgInfo& total_url_msg_info = total_url_iter_ret.first->second;
    1769                 :        345 :   Impl::UrlMsgInfo& url_msg_info = url_iter_ret.first->second;
    1770                 :        345 :   auto resolved_schema_type = SchemaData::resolve_type(schema_type, ser_type);
    1771                 :            : 
    1772         [ +  - ]:        345 :   std::string next_ser_type = total_url_msg_info.ser_type;
    1773                 :        345 :   SchemaType next_schema_type = total_url_msg_info.schema_type;
    1774                 :            : 
    1775         [ +  + ]:        345 :   if (total_url_iter_ret.second) {
    1776         [ +  - ]:        181 :     next_ser_type = ser_type;
    1777                 :        181 :     next_schema_type = resolved_schema_type;
    1778                 :            :   } else {
    1779         [ +  + ]:        164 :     if (!ser_type.empty()) {
    1780         [ +  + ]:         71 :       if (next_ser_type.empty()) {
    1781         [ +  - ]:          1 :         next_ser_type = ser_type;
    1782         [ +  + ]:         70 :       } else if VUNLIKELY (next_ser_type != ser_type) {
    1783   [ +  -  +  - ]:          6 :         CLOG_E("VDBWriter: URL [%s] ser changed from [%s] to [%s].", url.c_str(), next_ser_type.c_str(),
    1784                 :            :                ser_type.c_str());
    1785         [ +  - ]:          3 :         discard_new_url_entries();
    1786                 :          3 :         return false;
    1787                 :            :       }
    1788                 :            :     }
    1789                 :            :   }
    1790                 :            : 
    1791         [ +  + ]:        342 :   if (!next_ser_type.empty()) {
    1792                 :        242 :     std::string schema_ser_type;
    1793         [ -  + ]:        242 :     const auto schema_ser_source = ser_type.empty() ? std::string_view{next_ser_type} : std::string_view{ser_type};
    1794                 :        242 :     SchemaType schema_storage_type = SchemaData::resolve_type(schema_type, schema_ser_source);
    1795                 :        242 :     bool has_split_method_schema = false;
    1796                 :            : 
    1797         [ +  - ]:        242 :     schema_ser_type.assign(schema_ser_source.begin(), schema_ser_source.end());
    1798                 :            : 
    1799   [ +  +  +  + ]:        210 :     if ((action_type == ActionType::kClientRequest || action_type == ActionType::kClientResponse ||
    1800   [ +  +  +  +  :        485 :          action_type == ActionType::kServerRequest || action_type == ActionType::kServerResponse) &&
                   +  + ]
    1801         [ +  - ]:         36 :         !schema_ser_source.empty()) {
    1802                 :         36 :       const auto split_pos = schema_ser_source.find('|');
    1803                 :            : 
    1804         [ +  - ]:         36 :       if (split_pos != std::string_view::npos) {
    1805         [ +  - ]:         36 :         auto payload_ser_type = schema_ser_source.substr(0, split_pos);
    1806                 :            : 
    1807   [ +  +  +  + ]:         36 :         if (action_type == ActionType::kClientResponse || action_type == ActionType::kServerResponse) {
    1808         [ +  - ]:          3 :           payload_ser_type = schema_ser_source.substr(split_pos + 1);
    1809                 :            :         }
    1810                 :            : 
    1811         [ +  - ]:         36 :         if (!payload_ser_type.empty()) {
    1812         [ +  - ]:         36 :           schema_ser_type.assign(payload_ser_type.begin(), payload_ser_type.end());
    1813                 :         36 :           schema_storage_type = SchemaData::resolve_type(schema_type, payload_ser_type);
    1814                 :         36 :           has_split_method_schema = true;
    1815                 :            :         }
    1816                 :            :       }
    1817                 :            :     }
    1818                 :        242 :     SchemaData schema_data;
    1819                 :            : 
    1820   [ +  -  +  + ]:        242 :     if VUNLIKELY (!load_schema(schema_ser_type, schema_storage_type, schema_data)) {
    1821         [ +  - ]:          2 :       discard_new_url_entries();
    1822                 :          2 :       return false;
    1823                 :            :     }
    1824                 :            : 
    1825                 :        240 :     schema_storage_type = SchemaData::resolve_type(schema_storage_type, schema_ser_type, schema_data.encoding);
    1826                 :            : 
    1827         [ +  + ]:        240 :     if (has_split_method_schema) {
    1828         [ +  + ]:         36 :       if (schema_storage_type != SchemaType::kUnknown) {
    1829         [ -  + ]:          6 :         if (next_schema_type == SchemaType::kUnknown) {
    1830                 :            :           next_schema_type = schema_storage_type;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1831         [ -  + ]:          6 :         } else if (next_schema_type != schema_storage_type) {
    1832                 :            :           next_schema_type = SchemaType::kUnknown;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1833                 :            :         }
    1834                 :            :       }
    1835                 :            :     } else {
    1836         [ +  + ]:        204 :       if (resolved_schema_type == SchemaType::kUnknown) {
    1837                 :            :         const auto inferred_schema_type =
    1838                 :          1 :             SchemaData::resolve_type(schema_data.schema_type, schema_data.name, schema_data.encoding);
    1839                 :            : 
    1840         [ +  - ]:          1 :         if (inferred_schema_type != SchemaType::kUnknown) {
    1841                 :          1 :           resolved_schema_type = inferred_schema_type;
    1842                 :            :         }
    1843                 :            :       }
    1844                 :            : 
    1845         [ +  - ]:        204 :       if (resolved_schema_type != SchemaType::kUnknown) {
    1846         [ +  + ]:        204 :         if (next_schema_type == SchemaType::kUnknown) {
    1847                 :          2 :           next_schema_type = resolved_schema_type;
    1848         [ -  + ]:        202 :         } else if VUNLIKELY (next_schema_type != resolved_schema_type) {
    1849                 :            :           // LCOV_EXCL_START GCOVR_EXCL_START
    1850                 :            :           CLOG_E("VDBWriter: URL [%s] schema changed from [%d] to [%d].", url.c_str(),
    1851                 :            :                  static_cast<int>(next_schema_type), static_cast<int>(resolved_schema_type));
    1852                 :            :           discard_new_url_entries();
    1853                 :            :           return false;
    1854                 :            :           // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1855                 :            :         }
    1856                 :            :       }
    1857                 :            :     }
    1858                 :            : 
    1859         [ +  - ]:        240 :     std::string schema_key = schema_ser_type;
    1860         [ +  - ]:        240 :     schema_key.push_back('\x1F');
    1861         [ +  - ]:        240 :     schema_key.append(SchemaData::convert_type(schema_storage_type));
    1862                 :            : 
    1863   [ +  -  +  + ]:        240 :     if (impl_->ser_map.find(schema_key) == impl_->ser_map.end()) {
    1864                 :            :       const bool has_schema_blob =
    1865   [ +  +  +  -  :        156 :           !schema_data.name.empty() && !schema_data.encoding.empty() && !schema_data.data.empty();
                   +  - ]
    1866                 :            : 
    1867         [ +  + ]:        156 :       if (has_schema_blob) {
    1868   [ +  -  -  + ]:          8 :         if VUNLIKELY (!insert_schema(schema_data)) {
    1869                 :            :           rollback_cache();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1870                 :            :           return false;      // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1871                 :            :         }
    1872                 :            :       }
    1873                 :            : 
    1874         [ +  - ]:        156 :       impl_->ser_map.emplace(schema_key);
    1875                 :            :     }
    1876   [ +  -  +  +  :        244 :   }
                   +  + ]
    1877                 :            : 
    1878         [ +  + ]:        340 :   if (total_url_iter_ret.second) {
    1879                 :        180 :     total_url_msg_info.index = impl_->total_url_map.size() - 1;
    1880         [ +  - ]:        180 :     impl_->total_url_list.emplace_back(url);
    1881                 :            :   }
    1882                 :            : 
    1883         [ +  + ]:        340 :   if (url_iter_ret.second) {
    1884                 :            :     // insert url
    1885                 :        208 :     url_msg_info.index = impl_->url_map.size() - 1;
    1886                 :            : 
    1887         [ +  + ]:        208 :     if (!total_url_iter_ret.second) {
    1888                 :         28 :       total_url_msg_info.previous_segment_last_timestamp = total_url_msg_info.last_timestamp;
    1889                 :            :     }
    1890                 :            : 
    1891   [ +  +  +  -  :        208 :     if (action_type == ActionType::kClientRequest || action_type == ActionType::kClientResponse ||
                   +  - ]
    1892         [ -  + ]:        176 :         action_type == ActionType::kServerRequest ||
    1893                 :            :         action_type == ActionType::kServerResponse) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1894         [ +  - ]:         32 :       impl_->write_url_type = "Method";
    1895   [ +  +  +  + ]:        176 :     } else if (action_type == ActionType::kPublish || action_type == ActionType::kSubscribe) {
    1896         [ +  - ]:        141 :       impl_->write_url_type = "Event";
    1897   [ +  +  +  + ]:         35 :     } else if (action_type == ActionType::kSet || action_type == ActionType::kGet) {
    1898         [ +  - ]:         34 :       impl_->write_url_type = "Field";
    1899                 :            :     } else {
    1900         [ +  - ]:          1 :       impl_->write_url_type = "Unknown";
    1901                 :            :     }
    1902                 :            : 
    1903         [ +  - ]:        208 :     ::sqlite3_bind_int(impl_->urls_stmt, get_column(0), total_url_msg_info.index);
    1904         [ +  - ]:        208 :     ::sqlite3_bind_text(impl_->urls_stmt, get_column(1), url.c_str(), url.size(), SQLITE_STATIC);
    1905         [ +  - ]:        208 :     ::sqlite3_bind_text(impl_->urls_stmt, get_column(2), impl_->write_url_type.c_str(), impl_->write_url_type.size(),
    1906                 :            :                         SQLITE_STATIC);
    1907         [ +  - ]:        208 :     ::sqlite3_bind_text(impl_->urls_stmt, get_column(3), next_ser_type.c_str(), next_ser_type.size(), SQLITE_STATIC);
    1908         [ +  - ]:        208 :     const std::string next_encoding(SchemaData::convert_type(next_schema_type));
    1909         [ +  - ]:        208 :     ::sqlite3_bind_text(impl_->urls_stmt, get_column(4), next_encoding.c_str(), next_encoding.size(), SQLITE_STATIC);
    1910         [ +  - ]:        208 :     ::sqlite3_bind_int64(impl_->urls_stmt, get_column(5), 0);
    1911         [ +  - ]:        208 :     ::sqlite3_bind_double(impl_->urls_stmt, get_column(6), 0);
    1912         [ +  - ]:        208 :     ::sqlite3_bind_int64(impl_->urls_stmt, get_column(7), 0);
    1913         [ +  - ]:        208 :     ::sqlite3_bind_double(impl_->urls_stmt, get_column(8), 0);
    1914                 :            : 
    1915         [ +  - ]:        208 :     ret = ::sqlite3_step(impl_->urls_stmt);
    1916                 :            : 
    1917         [ -  + ]:        208 :     if VUNLIKELY (ret != SQLITE_DONE) {
    1918                 :            :       CLOG_W("Failed to insert urls table: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1919                 :            :       rollback_cache();                                                         // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1920                 :            :       return false;                                                             // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1921                 :            :     }
    1922                 :            : 
    1923         [ +  - ]:        208 :     ret = ::sqlite3_reset(impl_->urls_stmt);
    1924                 :            : 
    1925         [ -  + ]:        208 :     if VUNLIKELY (ret != SQLITE_OK) {
    1926                 :            :       CLOG_W("Failed to reset urls table: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1927                 :            :       rollback_cache();                                                        // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1928                 :            :       return false;                                                            // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1929                 :            :     }
    1930                 :            : 
    1931         [ +  - ]:        208 :     url_msg_info.url_type = impl_->write_url_type;
    1932         [ +  - ]:        208 :     total_url_msg_info.url_type = impl_->write_url_type;
    1933                 :            : 
    1934         [ +  + ]:        208 :     if (!next_ser_type.empty()) {
    1935         [ +  - ]:        201 :       url_msg_info.ser_type = next_ser_type;
    1936         [ +  - ]:        201 :       total_url_msg_info.ser_type = next_ser_type;
    1937                 :            :     }
    1938                 :            : 
    1939                 :        208 :     url_msg_info.schema_type = next_schema_type;
    1940                 :        208 :     total_url_msg_info.schema_type = next_schema_type;
    1941   [ +  -  +  +  :        340 :   } else if (total_url_msg_info.ser_type != next_ser_type || total_url_msg_info.schema_type != next_schema_type) {
             -  +  +  + ]
    1942                 :          1 :     const bool ser_changed = total_url_msg_info.ser_type != next_ser_type;
    1943                 :          1 :     const bool schema_changed = total_url_msg_info.schema_type != next_schema_type;
    1944                 :            : 
    1945   [ -  +  -  - ]:          1 :     if (ser_changed || schema_changed) {
    1946         [ +  - ]:          1 :       ::sqlite3_bind_text(impl_->update_url_meta_stmt, get_column(0), next_ser_type.c_str(), next_ser_type.size(),
    1947                 :            :                           SQLITE_STATIC);
    1948         [ +  - ]:          1 :       const std::string next_encoding(SchemaData::convert_type(next_schema_type));
    1949         [ +  - ]:          1 :       ::sqlite3_bind_text(impl_->update_url_meta_stmt, get_column(1), next_encoding.c_str(), next_encoding.size(),
    1950                 :            :                           SQLITE_STATIC);
    1951         [ +  - ]:          1 :       ::sqlite3_bind_text(impl_->update_url_meta_stmt, get_column(2), url.c_str(), url.size(), SQLITE_STATIC);
    1952                 :            : 
    1953         [ +  - ]:          1 :       ret = ::sqlite3_step(impl_->update_url_meta_stmt);
    1954                 :            : 
    1955         [ -  + ]:          1 :       if VUNLIKELY (ret != SQLITE_DONE) {
    1956                 :            :         // LCOV_EXCL_START GCOVR_EXCL_START
    1957                 :            :         CLOG_W("Failed to update urls metadata table: %s.", ::sqlite3_errmsg(impl_->db));
    1958                 :            :         rollback_cache();
    1959                 :            :         return false;
    1960                 :            :         // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1961                 :            :       }
    1962                 :            : 
    1963         [ +  - ]:          1 :       ret = ::sqlite3_reset(impl_->update_url_meta_stmt);
    1964                 :            : 
    1965         [ -  + ]:          1 :       if VUNLIKELY (ret != SQLITE_OK) {
    1966                 :            :         // LCOV_EXCL_START GCOVR_EXCL_START
    1967                 :            :         CLOG_W("Failed to reset urls metadata table: %s.", ::sqlite3_errmsg(impl_->db));
    1968                 :            :         rollback_cache();
    1969                 :            :         return false;
    1970                 :            :         // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1971                 :            :       }
    1972                 :            : 
    1973         [ +  - ]:          1 :       total_url_msg_info.ser_type = next_ser_type;
    1974         [ +  - ]:          1 :       url_msg_info.ser_type = next_ser_type;
    1975                 :          1 :       total_url_msg_info.schema_type = next_schema_type;
    1976                 :          1 :       url_msg_info.schema_type = next_schema_type;
    1977         [ +  - ]:          1 :     }
    1978                 :            :   }
    1979                 :            : 
    1980                 :            :   // update info
    1981                 :        340 :   ++url_msg_info.count;
    1982                 :        340 :   ++total_url_msg_info.count;
    1983                 :            : 
    1984                 :        340 :   impl_->cached_size.fetch_add(data.size(), std::memory_order_relaxed);
    1985                 :            : 
    1986                 :        340 :   ++impl_->current_row;
    1987                 :        340 :   ++impl_->total_current_row;
    1988                 :        340 :   impl_->total_timestamp = std::max(impl_->total_timestamp, microseconds_timestamp);
    1989                 :            : 
    1990   [ +  +  +  +  :        340 :   if (action_type == ActionType::kPublish || action_type == ActionType::kSubscribe || action_type == ActionType::kSet ||
             +  +  +  + ]
    1991                 :            :       action_type == ActionType::kGet) {
    1992                 :        303 :     double time_duration = 0;
    1993                 :            : 
    1994         [ +  + ]:        303 :     if (total_url_msg_info.first_timestamp < 0) {
    1995                 :        153 :       total_url_msg_info.first_timestamp = microseconds_timestamp;
    1996                 :            :     }
    1997                 :            : 
    1998                 :        303 :     total_url_msg_info.last_timestamp = microseconds_timestamp;
    1999                 :        303 :     time_duration = (total_url_msg_info.last_timestamp - total_url_msg_info.first_timestamp) / 1000'000.0;
    2000                 :            : 
    2001         [ +  + ]:        303 :     if (time_duration > 0) {
    2002                 :        149 :       total_url_msg_info.freq = total_url_msg_info.count / time_duration;
    2003                 :            :     } else {
    2004                 :        154 :       total_url_msg_info.freq = 0;
    2005                 :            :     }
    2006                 :            : 
    2007         [ +  + ]:        303 :     if (url_msg_info.first_timestamp < 0) {
    2008                 :        181 :       url_msg_info.first_timestamp = microseconds_timestamp;
    2009                 :            :     }
    2010                 :            : 
    2011                 :        303 :     url_msg_info.last_timestamp = microseconds_timestamp;
    2012                 :        303 :     time_duration = (url_msg_info.last_timestamp - url_msg_info.first_timestamp) / 1000'000.0;
    2013                 :            : 
    2014         [ +  + ]:        303 :     if (time_duration > 0) {
    2015                 :        121 :       url_msg_info.freq = url_msg_info.count / time_duration;
    2016                 :            :     } else {
    2017                 :        182 :       url_msg_info.freq = 0;
    2018                 :            :     }
    2019                 :            :   }
    2020                 :            : 
    2021                 :            :   // insert data
    2022         [ +  - ]:        340 :   const auto write_action_type = convert_action(action_type);
    2023                 :            : 
    2024                 :        340 :   impl_->last_timestamp = std::max(impl_->last_timestamp, microseconds_timestamp);
    2025                 :            : 
    2026         [ +  - ]:        340 :   ::sqlite3_bind_int64(impl_->datas_stmt, get_column(0), microseconds_timestamp);
    2027         [ +  - ]:        340 :   ::sqlite3_bind_int(impl_->datas_stmt, get_column(1), total_url_msg_info.index);
    2028         [ +  - ]:        340 :   ::sqlite3_bind_text(impl_->datas_stmt, get_column(2), write_action_type.data(), write_action_type.size(),
    2029                 :            :                       SQLITE_STATIC);
    2030                 :            : 
    2031                 :            :   // check compress
    2032                 :        340 :   do_compress = false;
    2033                 :            : 
    2034   [ +  +  +  -  :        367 :   if (impl_->enable_compressed && static_cast<int64_t>(data.size()) >= impl_->config.compress_start_size &&
                   +  + ]
    2035   [ +  -  +  + ]:         27 :       impl_->config.ignore_compress_urls.count(url) == 0) {
    2036         [ +  - ]:         18 :     auto& compress_ignore_cnt = impl_->compress_ignore_map[url];
    2037                 :            : 
    2038         [ +  + ]:         18 :     if (compress_ignore_cnt < kCompressMaxIgnoreCnt) {
    2039                 :         17 :       compressed_data = Bytes::compress_data(data.data(), data.size(), impl_->config.compress_level > 3);
    2040                 :            : 
    2041   [ +  -  +  +  :         17 :       if (!compressed_data.empty() && compressed_data.size() < data.size() * kCompressMaxRatio) {
                   +  + ]
    2042                 :         12 :         do_compress = true;
    2043                 :         12 :         compress_ignore_cnt = 0;
    2044                 :            :       } else {
    2045                 :          5 :         ++compress_ignore_cnt;
    2046                 :            :       }
    2047                 :            :     }
    2048                 :            :   }
    2049                 :            : 
    2050         [ +  + ]:        340 :   if (do_compress) {
    2051         [ +  - ]:         12 :     ::sqlite3_bind_blob(impl_->datas_stmt, get_column(3), compressed_data.data(), compressed_data.size(),
    2052                 :            :                         SQLITE_STATIC);
    2053                 :            :   } else {
    2054         [ +  - ]:        328 :     ::sqlite3_bind_blob(impl_->datas_stmt, get_column(3), data.data(), data.size(), SQLITE_STATIC);
    2055                 :            :   }
    2056                 :            : 
    2057                 :        340 :   auto accounted_size = data.size();
    2058                 :            : 
    2059   [ +  +  +  +  :        340 :   if VUNLIKELY (!do_compress && impl_->enable_compressed && Bytes::is_compress_data(data.data(), data.size())) {
          +  +  +  +  +  
                      + ]
    2060                 :          6 :     accounted_size = (static_cast<size_t>(data[4]) << 24U) | (static_cast<size_t>(data[5]) << 16U) |
    2061                 :          6 :                      (static_cast<size_t>(data[6]) << 8U) | static_cast<size_t>(data[7]);
    2062                 :            :   }
    2063                 :            : 
    2064                 :        340 :   url_msg_info.size += accounted_size;
    2065                 :        340 :   total_url_msg_info.size += accounted_size;
    2066                 :        340 :   impl_->current_size += static_cast<int64_t>(accounted_size);
    2067                 :        340 :   impl_->total_current_size += static_cast<int64_t>(accounted_size);
    2068                 :            : 
    2069         [ +  - ]:        340 :   ret = ::sqlite3_step(impl_->datas_stmt);
    2070                 :            : 
    2071         [ -  + ]:        340 :   if VUNLIKELY (ret != SQLITE_DONE) {
    2072                 :            :     CLOG_W("Failed to insert datas table: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2073                 :            :     rollback_cache();                                                          // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2074                 :            :     return false;                                                              // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2075                 :            :   }
    2076                 :            : 
    2077         [ +  - ]:        340 :   ret = ::sqlite3_reset(impl_->datas_stmt);
    2078                 :            : 
    2079         [ -  + ]:        340 :   if VUNLIKELY (ret != SQLITE_OK) {
    2080                 :            :     CLOG_W("Failed to reset datas table: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2081                 :            :     rollback_cache();                                                         // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2082                 :            :     return false;                                                             // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2083                 :            :   }
    2084                 :            : 
    2085                 :            :   // update on wal_mode
    2086                 :            : 
    2087         [ +  + ]:        340 :   if (impl_->sync_timer.get() > kSyncWriteInterval) {
    2088                 :          1 :     impl_->sync_timer.restart();
    2089                 :            : 
    2090         [ +  - ]:          1 :     ::sqlite3_bind_int64(impl_->update_urls_stmt, get_column(0), url_msg_info.count);
    2091         [ +  - ]:          1 :     ::sqlite3_bind_int64(impl_->update_urls_stmt, get_column(1), url_msg_info.size);
    2092         [ +  - ]:          1 :     ::sqlite3_bind_double(impl_->update_urls_stmt, get_column(2), url_msg_info.freq);
    2093         [ +  - ]:          1 :     ::sqlite3_bind_text(impl_->update_urls_stmt, get_column(3), url.c_str(), url.size(), SQLITE_STATIC);
    2094         [ +  - ]:          1 :     ret = ::sqlite3_step(impl_->update_urls_stmt);
    2095                 :            : 
    2096         [ -  + ]:          1 :     if VUNLIKELY (ret != SQLITE_DONE) {
    2097                 :            :       CLOG_W("Failed to update urls table: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2098                 :            :       rollback_cache();                                                         // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2099                 :            :       return false;                                                             // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2100                 :            :     }
    2101                 :            : 
    2102         [ +  - ]:          1 :     ret = ::sqlite3_reset(impl_->update_urls_stmt);
    2103                 :            : 
    2104         [ -  + ]:          1 :     if VUNLIKELY (ret != SQLITE_OK) {
    2105                 :            :       CLOG_W("Failed to reset urls table: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2106                 :            :       rollback_cache();                                                        // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2107                 :            :       return false;                                                            // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2108                 :            :     }
    2109                 :            : 
    2110         [ +  - ]:          1 :     ::sqlite3_bind_int64(impl_->update_header_stmt, get_column(0), impl_->current_row);
    2111         [ +  - ]:          1 :     ::sqlite3_bind_int64(impl_->update_header_stmt, get_column(1), impl_->last_timestamp);
    2112         [ +  - ]:          1 :     ret = ::sqlite3_step(impl_->update_header_stmt);
    2113                 :            : 
    2114         [ -  + ]:          1 :     if VUNLIKELY (ret != SQLITE_DONE) {
    2115                 :            :       CLOG_W("Failed to update header table: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2116                 :            :       rollback_cache();                                                           // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2117                 :            :       return false;                                                               // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2118                 :            :     }
    2119                 :            : 
    2120         [ +  - ]:          1 :     ret = ::sqlite3_reset(impl_->update_header_stmt);
    2121                 :            : 
    2122         [ -  + ]:          1 :     if VUNLIKELY (ret != SQLITE_OK) {
    2123                 :            :       CLOG_W("Failed to reset header table: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2124                 :            :       rollback_cache();                                                          // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2125                 :            :       return false;                                                              // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2126                 :            :     }
    2127                 :            :   }
    2128                 :            : 
    2129         [ +  + ]:        680 :   if VUNLIKELY (impl_->cached_size.load(std::memory_order_relaxed) > impl_->config.cache_size) {
    2130   [ +  -  -  + ]:         46 :     if VUNLIKELY (!sync_cache()) {
    2131                 :            :       return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2132                 :            :     }
    2133                 :            :   }
    2134                 :            : 
    2135                 :        340 :   return true;
    2136                 :            : #else
    2137                 :            :   (void)url;
    2138                 :            :   (void)ser_type;
    2139                 :            :   (void)schema_type;
    2140                 :            :   (void)action_type;
    2141                 :            :   (void)data;
    2142                 :            :   (void)microseconds_timestamp;
    2143                 :            :   return false;
    2144                 :            : #endif
    2145                 :        347 : }
    2146                 :            : 
    2147                 :         98 : bool VDBWriter::write_filex(bool complete) {
    2148                 :            : #ifdef _WIN32
    2149                 :            :   std::filesystem::path file_path(Helpers::string_to_wstring(impl_->path));
    2150                 :            : #else
    2151         [ +  - ]:         98 :   std::filesystem::path file_path(impl_->path);
    2152                 :            : #endif
    2153                 :            : 
    2154                 :            :   try {
    2155                 :         98 :     nlohmann::ordered_json json;
    2156                 :            : 
    2157         [ +  - ]:         98 :     json["VLinkHeader"] = {
    2158                 :            :         {"major", VLINK_VERSION_MAJOR},  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2159                 :            :         {"minor", VLINK_VERSION_MINOR},  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2160                 :            :         {"patch", VLINK_VERSION_PATCH},  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2161                 :         98 :         {"count", impl_->total_current_row},
    2162                 :         98 :         {"duration", impl_->total_timestamp},
    2163                 :            :         {"accuracy", "MicroSecond"},
    2164                 :         98 :         {"compress", impl_->enable_compressed ? "lzav" : "None"},
    2165                 :         98 :         {"process", impl_->app_name},
    2166         [ +  - ]:         98 :         {"date", get_format_date(&impl_->time_start)},
    2167                 :         98 :         {"tag", impl_->tag_name},
    2168         [ +  + ]:         98 :         {"split_by_size", impl_->config.split_by_time > 0 ? 0 : impl_->config.split_by_size},
    2169                 :         98 :         {"split_by_time", impl_->config.split_by_time},
    2170                 :            :         {"complete", complete},
    2171                 :         98 :         {"timezone", impl_->timezone_diff},
    2172                 :         98 :         {"start_timestamp", impl_->start_timestamp},
    2173   [ +  -  +  -  :       5586 :     };
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
             -  -  -  - ]
    2174                 :            : 
    2175                 :         98 :     nlohmann::ordered_json url_json;
    2176                 :            : 
    2177                 :            :     {
    2178   [ +  -  +  - ]:         98 :       std::lock_guard lock(sample_mutex());
    2179                 :            : 
    2180         [ +  + ]:        164 :       for (const auto& url : impl_->total_url_list) {
    2181         [ +  - ]:         66 :         const auto& ext_info = impl_->total_url_map[url];
    2182   [ +  -  +  -  :         66 :         auto loss = total_url_loss_map_ref()[recover_recorded_url(url)];
                   +  - ]
    2183                 :            : 
    2184   [ +  -  +  -  :       2376 :         url_json.push_back({
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
                -  -  - ]
    2185                 :            :             // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2186                 :         66 :             {"index", ext_info.index},
    2187                 :            :             {"url", url},
    2188         [ +  - ]:         66 :             {"type", ext_info.url_type},
    2189         [ +  - ]:         66 :             {"ser", ext_info.ser_type},
    2190         [ +  - ]:        132 :             {"encoding", std::string(SchemaData::convert_type(ext_info.schema_type))},
    2191                 :         66 :             {"count", ext_info.count},
    2192                 :         66 :             {"size", ext_info.size},
    2193                 :            :             {"loss", loss},
    2194                 :         66 :             {"freq", ext_info.freq},
    2195                 :            :         });
    2196                 :            :       }
    2197                 :         98 :     }
    2198                 :            : 
    2199         [ +  - ]:         98 :     json["VLinkUrls"] = std::move(url_json);
    2200                 :            : 
    2201                 :         98 :     nlohmann::ordered_json files_json;
    2202         [ +  + ]:        195 :     for (const auto& file : impl_->split_file_list) {
    2203   [ +  -  +  - ]:         97 :       files_json.push_back(file);
    2204                 :            :     }
    2205                 :            : 
    2206         [ +  - ]:         98 :     json["VLinkFiles"] = std::move(files_json);
    2207                 :            : 
    2208         [ +  - ]:         98 :     std::ofstream filex(file_path);
    2209                 :            : 
    2210   [ +  -  -  + ]:         98 :     if VUNLIKELY (!filex.is_open()) {
    2211                 :            :       return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2212                 :            :     }
    2213                 :            : 
    2214   [ +  -  +  - ]:         98 :     filex << json.dump(4);
    2215         [ +  - ]:         98 :     filex.close();
    2216                 :            : 
    2217   [ +  -  -  + ]:         98 :     if VUNLIKELY (!filex) {
    2218                 :            :       return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2219                 :            :     }
    2220   [ +  -  +  -  :         98 :   } catch (const nlohmann::json::exception& e) {
          +  -  +  -  -  
                      - ]
    2221                 :            :     CLOG_W("VDBWriter: JSON error during config export: %s.", e.what());  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2222                 :            :     return false;                                                         // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2223                 :            :   }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2224                 :            : 
    2225                 :         98 :   return true;
    2226                 :         98 : }
    2227                 :            : 
    2228                 :        678 : bool VDBWriter::begin_cache() {
    2229                 :            : #ifdef VLINK_ENABLE_SQLITE
    2230                 :            : 
    2231         [ -  + ]:        678 :   if VUNLIKELY (!impl_->db) {
    2232                 :            :     VLOG_W("VDBWriter: Sqlite not open.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2233                 :            :     return false;                           // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2234                 :            :   }
    2235                 :            : 
    2236         [ +  + ]:        678 :   if VLIKELY (impl_->in_cached.load(std::memory_order_relaxed)) {
    2237                 :        213 :     return true;
    2238                 :            :   }
    2239                 :            : 
    2240                 :        465 :   int ret = ::sqlite3_exec(impl_->db, "BEGIN;", nullptr, nullptr, nullptr);
    2241                 :            : 
    2242         [ -  + ]:        465 :   if VUNLIKELY (ret != SQLITE_OK) {
    2243                 :            :     CLOG_W("Failed to begin transaction: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2244                 :            :     return false;                                                             // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2245                 :            :   }
    2246                 :            : 
    2247                 :        465 :   impl_->cache_snapshot = std::make_unique<Impl::WriteStateSnapshot>(*impl_);
    2248                 :        465 :   impl_->in_cached.store(true, std::memory_order_relaxed);
    2249                 :        465 :   impl_->cached_size.store(0, std::memory_order_relaxed);
    2250                 :            : 
    2251                 :        465 :   impl_->cache_timer.restart();
    2252                 :            : 
    2253                 :        465 :   return true;
    2254                 :            : #else
    2255                 :            :   return false;
    2256                 :            : #endif
    2257                 :            : }
    2258                 :            : 
    2259                 :        528 : bool VDBWriter::sync_cache() {
    2260                 :            : #ifdef VLINK_ENABLE_SQLITE
    2261                 :            : 
    2262         [ -  + ]:        528 :   if VUNLIKELY (!impl_->db) {
    2263                 :            :     VLOG_W("VDBWriter: Sqlite not open.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2264                 :            :     return false;                           // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2265                 :            :   }
    2266                 :            : 
    2267         [ +  + ]:        528 :   if (!impl_->in_cached.load(std::memory_order_relaxed)) {
    2268                 :         65 :     return true;
    2269                 :            :   }
    2270                 :            : 
    2271                 :        463 :   int ret = ::sqlite3_exec(impl_->db, "COMMIT;", nullptr, nullptr, nullptr);
    2272                 :            : 
    2273         [ -  + ]:        463 :   if VUNLIKELY (ret != SQLITE_OK) {
    2274                 :            :     CLOG_W("Failed to commit transaction: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2275                 :            :     return false;                                                              // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2276                 :            :   }
    2277                 :            : 
    2278                 :        463 :   impl_->in_cached.store(false, std::memory_order_relaxed);
    2279                 :        463 :   impl_->cached_size.store(0, std::memory_order_relaxed);
    2280                 :        463 :   impl_->cache_snapshot.reset();
    2281                 :            : 
    2282                 :        463 :   impl_->cache_timer.stop();
    2283                 :            : 
    2284                 :        463 :   return true;
    2285                 :            : #else
    2286                 :            :   return false;
    2287                 :            : #endif
    2288                 :            : }
    2289                 :            : 
    2290                 :          2 : bool VDBWriter::rollback_cache() {
    2291                 :            : #ifdef VLINK_ENABLE_SQLITE
    2292                 :            : 
    2293         [ -  + ]:          2 :   if VUNLIKELY (!impl_->db) {
    2294                 :            :     VLOG_W("VDBWriter: Sqlite not open.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2295                 :            :     return false;                           // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2296                 :            :   }
    2297                 :            : 
    2298                 :          2 :   ::sqlite3_exec(impl_->db, "ROLLBACK;", nullptr, nullptr, nullptr);
    2299                 :            : 
    2300         [ +  - ]:          2 :   if (impl_->cache_snapshot) {
    2301                 :          2 :     impl_->cache_snapshot->restore(*impl_);
    2302                 :          2 :     impl_->cache_snapshot.reset();
    2303                 :            :   }
    2304                 :            : 
    2305                 :          2 :   impl_->in_cached.store(false, std::memory_order_relaxed);
    2306                 :          2 :   impl_->cached_size.store(0, std::memory_order_relaxed);
    2307                 :            : 
    2308                 :          2 :   impl_->cache_timer.stop();
    2309                 :            : 
    2310                 :          2 :   return true;
    2311                 :            : #else
    2312                 :            :   return false;
    2313                 :            : #endif
    2314                 :            : }
    2315                 :            : 
    2316                 :         44 : bool VDBWriter::insert_schema(const SchemaData& schema_data) {
    2317                 :            : #ifdef VLINK_ENABLE_SQLITE
    2318                 :            : 
    2319         [ -  + ]:         44 :   if VUNLIKELY (!impl_->db) {
    2320                 :            :     VLOG_W("VDBWriter: Sqlite not open.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2321                 :            :     return false;                           // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2322                 :            :   }
    2323                 :            : 
    2324         [ +  - ]:         44 :   std::string encoding = schema_data.encoding;
    2325                 :         44 :   SchemaType schema_type = SchemaData::resolve_type(schema_data.schema_type, schema_data.name, encoding);
    2326                 :            : 
    2327   [ -  +  -  -  :         44 :   if (encoding.empty() && SchemaData::is_real_type(schema_type)) {
                   -  + ]
    2328                 :            :     encoding = std::string(SchemaData::convert_type(schema_type));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2329                 :            :   }
    2330                 :            : 
    2331   [ +  -  +  -  :         44 :   if (schema_data.name.empty() || encoding.empty() || schema_data.data.empty()) {
             -  +  -  + ]
    2332                 :            :     return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2333                 :            :   }
    2334                 :            : 
    2335                 :         44 :   int ret = 0;
    2336                 :            : 
    2337         [ +  - ]:         44 :   ::sqlite3_bind_text(impl_->schemas_stmt, get_column(0), schema_data.name.c_str(), schema_data.name.size(),
    2338                 :            :                       SQLITE_STATIC);
    2339                 :            : 
    2340         [ +  - ]:         44 :   ::sqlite3_bind_text(impl_->schemas_stmt, get_column(1), encoding.c_str(), encoding.size(), SQLITE_STATIC);
    2341                 :            : 
    2342         [ +  - ]:         44 :   ::sqlite3_bind_blob(impl_->schemas_stmt, get_column(2), schema_data.data.data(), schema_data.data.size(),
    2343                 :            :                       SQLITE_STATIC);
    2344                 :            : 
    2345         [ +  - ]:         44 :   ret = ::sqlite3_step(impl_->schemas_stmt);
    2346                 :            : 
    2347         [ -  + ]:         44 :   if VUNLIKELY (ret != SQLITE_DONE) {
    2348                 :            :     CLOG_W("Failed to insert schema table: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2349                 :            :     rollback_cache();                                                           // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2350                 :            :     return false;                                                               // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2351                 :            :   }
    2352                 :            : 
    2353         [ +  - ]:         44 :   ret = ::sqlite3_reset(impl_->schemas_stmt);
    2354                 :            : 
    2355         [ -  + ]:         44 :   if VUNLIKELY (ret != SQLITE_OK) {
    2356                 :            :     CLOG_W("Failed to reset schema table: %s.", ::sqlite3_errmsg(impl_->db));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2357                 :            :     rollback_cache();                                                          // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2358                 :            :     return false;                                                              // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2359                 :            :   }
    2360                 :            : 
    2361                 :         44 :   return true;
    2362                 :            : #else
    2363                 :            :   (void)schema_data;
    2364                 :            :   return false;
    2365                 :            : #endif
    2366                 :         44 : }
    2367                 :            : 
    2368                 :            : }  // namespace vlink

Generated by: LCOV version 1.14