VLink  2.0.0
A high-performance communication middleware
bag_processor.h
浏览该文件的文档.
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 /**
25  * @file bag_processor.h
26  * @brief Data-plane-time sliding-window reorder buffer, a helper for @c BagPluginInterface plugins.
27  *
28  * @details
29  * @c BagProcessor is the building block a @c BagPluginInterface plugin uses to reorder frames by
30  * their @e true data-plane time before re-emitting them -- on the write side before a frame is
31  * persisted, on the read side before it is replayed. It is symmetric: the same engine serves both
32  * directions because both push frames in and forward the reordered output through @c do_callback().
33  *
34  * Two distinct times are at play, which can disagree (out-of-order recording, async I/O, sender-side
35  * batching, transport requeue):
36  *
37  * - @c Frame::timestamp -- the canonical record / playback time the frame carries downstream.
38  * - the @b data-plane time -- the true event time carried inside the payload header, passed @e
39  * separately as the @c push() reorder key. @c BagProcessor emits frames in ascending data-plane-time
40  * order and remaps @c Frame::timestamp onto that sorted data-plane-time axis.
41  *
42  * @c BagProcessor is a @e pure reorder buffer. The plugin extracts the data-plane time from the
43  * header (parsing or deserialising the payload as needed) and passes it to @c push(); @c BagProcessor
44  * never inspects the payload nor touches the serialisation layer. Any payload transform (compress /
45  * decompress, URL / serialisation-type rewrite) is likewise the plugin's job, applied before @c push()
46  * or inside the output callback before it forwards the frame.
47  *
48  * The oldest cached frame is released only once the data-plane-time span between the oldest and
49  * newest cached frames reaches @c Config::min_cache_time, giving a late-but-earlier frame a chance to
50  * slot ahead of already-cached later frames. A wall-clock fallback drains the cache when a producer
51  * goes silent, so the stream can always make progress. Data-plane timestamps passed to @c push() are
52  * in microseconds; @c Config time tunables are expressed in milliseconds and converted internally.
53  *
54  * @par Write-side example (reorder by header time, then persist)
55  * @code
56  * class MyWritePlugin : public vlink::BagPluginInterface {
57  * public:
58  * MyWritePlugin() {
59  * processor_.register_output_callback([this](const vlink::Frame& f) { do_callback(f); });
60  * }
61  *
62  * vlink::BagPluginInterface::VersionInfo get_version_info() const override {
63  * return {"my-write", "1.0.0", __DATE__, "", ""};
64  * }
65  *
66  * void on_write(const vlink::Frame& frame) override {
67  * const int64_t data_timestamp = parse_header_time(frame.data); // plugin extracts the data-plane time
68  * processor_.push(data_timestamp, frame); // buffered + reordered by data_timestamp
69  * }
70  *
71  * void flush() override { processor_.flush(); } // drain the buffered tail at teardown
72  *
73  * private:
74  * vlink::BagProcessor processor_;
75  * };
76  * @endcode
77  */
78 
79 #pragma once
80 
81 #include <cstdint>
82 #include <memory>
83 #include <mutex>
84 
85 #include "../impl/types.h"
86 
87 namespace vlink {
88 
89 /**
90  * @class BagProcessor
91  * @brief Time-sorted relay buffer keyed on the data-plane time, shared by read- and write-side plugins.
92  *
93  * @details
94  * Thread-safe in the sense that @c push() may be called concurrently from the host's loop thread(s);
95  * delivery to the @c OutputCallback happens on a dedicated worker thread owned by the processor.
96  */
98  public:
99  /**
100  * @brief Sink receiving one @c Frame in data-plane-time order on the worker thread.
101  *
102  * @details
103  * Invoked once @c Config::min_cache_time of data-plane time has accumulated ahead of the candidate
104  * frame (or a wall-clock drain has fired). The frame is moved out of the cache into the callback.
105  * The callback frame timestamp is remapped onto the sorted data-plane-time axis so it stays strictly
106  * increasing after the reorder.
107  */
109 
110  /**
111  * @struct Config
112  * @brief Tunables controlling the reorder buffer behaviour.
113  */
114  struct Config final {
115  int64_t min_cache_time{500}; ///< Data-plane-time span to accumulate, in milliseconds.
116  int64_t max_cache_size{1024LL * 1024LL * 256}; ///< Maximum total payload bytes held (default 256 MiB).
117  int64_t max_jump_time{60LL * 60LL * 1000LL}; ///< Max absolute data-plane-time jump, in milliseconds.
118 
119  Config() {} // NOLINT(modernize-use-equals-default)
120  };
121 
122  /**
123  * @brief Builds the processor.
124  *
125  * @param config Cache time-window and memory-budget tunables.
126  */
127  explicit BagProcessor(const Config& config = Config());
128 
129  /**
130  * @brief Drains remaining frames in data-plane-time order and joins the worker thread.
131  */
133 
134  /**
135  * @brief Sets the sink receiving reordered frames and starts the worker thread.
136  *
137  * @details
138  * The first registration before @c push() takes effect; later registrations are ignored.
139  *
140  * @param output_callback Sink invoked once per frame on the worker thread.
141  */
142  void register_output_callback(OutputCallback&& output_callback);
143 
144  /**
145  * @brief Inserts a frame into the data-plane-time-sorted cache.
146  *
147  * @details
148  * Safe to call from any thread after @c register_output_callback(). Frames are ordered by
149  * @p data_timestamp. Negative @p data_timestamp is filled from the previous data timestamp and the
150  * @c Frame::timestamp delta; without a previous anchor it stays -1 and sorts first. Output frame
151  * timestamps are remapped on the data-time axis and kept strictly increasing. The frame is copied
152  * into the owning cache.
153  *
154  * @param data_timestamp Reorder key in microseconds (the data-plane time), or negative when missing.
155  * @param frame Frame to cache; @c Frame::timestamp is the canonical time before remapping.
156  */
157  void push(int64_t data_timestamp, const Frame& frame);
158 
159  /**
160  * @brief Synchronously drains every currently-buffered frame to the sink, in data-plane-time order.
161  *
162  * @details
163  * Blocks until the cache is empty: the worker thread emits each queued frame through the registered
164  * output callback, then wakes the caller. A plugin forwards this from @c BagPluginInterface::flush()
165  * so buffered tail frames are emitted before teardown. A no-op before callback registration or shutdown.
166  */
167  void flush();
168 
169  private:
170  bool on_check();
171 
172  void on_output(std::unique_lock<std::mutex>& lock, bool at_end);
173 
174  void on_run();
175 
176  void on_exec(bool at_end);
177 
178  struct Impl;
179  std::unique_ptr<Impl> impl_;
180 
182 };
183 
184 } // namespace vlink
#define VLINK_EXPORT
Definition: macros.h:81
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
Definition: macros.h:174