LCOV - code coverage report
Current view: top level - src/extension - vdb_writer.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 809 810 99.9 %
Date: 2026-06-27 19:56:04 Functions: 39 41 95.1 %
Branches: 915 1546 59.2 %

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

Generated by: LCOV version 1.14