userver: userver/ydb/topic.hpp Source File
Loading...
Searching...
No Matches
topic.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/ydb/topic.hpp
4/// @brief YDB Topic client
5
6#include <atomic>
7#include <chrono>
8#include <cstdint>
9#include <memory>
10#include <optional>
11#include <string>
12#include <string_view>
13
14#include <ydb-cpp-sdk/client/topic/client.h>
15#include <ydb-cpp-sdk/client/topic/producer.h>
16#include <ydb-cpp-sdk/client/types/executor/executor.h>
17
18#include <userver/compiler/impl/lifetime.hpp>
19#include <userver/engine/deadline.hpp>
20
21USERVER_NAMESPACE_BEGIN
22
23namespace ydb {
24
25namespace impl {
26class Driver;
27struct TopicSettings;
28} // namespace impl
29
30/// @brief Read session used to connect to one or more topics for reading
31///
32/// @see https://ydb.tech/docs/en/reference/ydb-sdk/topic#reading
33///
34/// ## Example usage:
35///
36/// @ref samples/ydb_service/components/topic_reader.hpp
37/// @ref samples/ydb_service/components/topic_reader.cpp
38///
39/// @example samples/ydb_service/components/topic_reader.hpp
40/// @example samples/ydb_service/components/topic_reader.cpp
41class TopicReadSession final {
42public:
43 /// @cond
44 // For internal use only.
45 explicit TopicReadSession(std::shared_ptr<NYdb::NTopic::IReadSession> read_session);
46 /// @endcond
47
48 /// @brief Get read session events
49 ///
50 /// Waits until event occurs
51 /// @param max_events_count maximum events count in batch
52 /// @param max_size_bytes total size limit for data messages in batch
53 /// if not specified, read session chooses event batch size automatically
57 );
58
59 /// @brief Get read session events
60 ///
61 /// Waits until event occurs
62 /// @param settings ydb native read session settings
65 );
66
67 /// @brief Close read session
68 ///
69 /// Waits for all commit acknowledgments to arrive.
70 /// Force close after timeout
71 bool Close(std::chrono::milliseconds timeout);
72
73 /// @brief Get native read session
74 ///
75 /// @warning Use with care! Facilities from @ref userver/drivers/subscribable_futures.hpp can help
76 /// with non-blocking wait operations.
77 NYdb::NTopic::IReadSession& GetNativeTopicReadSession() USERVER_IMPL_LIFETIME_BOUND;
78
79private:
80 std::shared_ptr<NYdb::NTopic::IReadSession> read_session_;
81};
82
83/// @brief Write session used to connect to a topic for writting
84///
85/// @see https://ydb.tech/docs/en/reference/ydb-sdk/topic#write
86class TopicWriteSession final {
87public:
88 /// @cond
89 /// For internal use only.
90 explicit TopicWriteSession(std::shared_ptr<NYdb::NTopic::IWriteSession> write_session);
91 /// @endcond
92
93 /// @brief Wait for the next write session event
94 ///
95 /// Suspends the current coroutine until an event is available, then returns it without blocking the thread.
96 NYdb::NTopic::TWriteSessionEvent::TEvent GetEvent();
97
98 /// @brief Poll for a write session event without waiting
99 ///
100 /// Returns the next buffered event immediately if one is available, or `std::nullopt` if the event queue is empty.
101 /// Does not suspend the coroutine.
102 ///
103 /// @note Sometimes may return `std::nullopt` even if an event is available. Intended for use in loops.
104 std::optional<NYdb::NTopic::TWriteSessionEvent::TEvent> TryGetEvent();
105
106 /// @brief Write a messsage using a continuation token from TReadyToAcceptEvent
107 ///
108 /// Must be called only after receiving TReadyToAcceptEvent from GetEvent() or TryGetEvent().
109 void Write(NYdb::NTopic::TContinuationToken&& token, NYdb::NTopic::TWriteMessage&& message);
110
111 /// @brief Close write session
112 ///
113 /// Waits for all in-flights messages to be acknowledged.
114 /// Force closes after timeout
115 bool Close(std::chrono::milliseconds timeout);
116
117 /// @brief Get native write session
118 ///
119 /// @warning Use with care! Facilities from @ref userver/drivers/subscribable_futures.hpp can help
120 /// with non-blocking wait operations.
121 NYdb::NTopic::IWriteSession& GetNativeTopicWriteSession() USERVER_IMPL_LIFETIME_BOUND;
122
123private:
124 std::shared_ptr<NYdb::NTopic::IWriteSession> write_session_;
125};
126
127/// @brief Simple write session used to write messages to a topic without
128/// manually handling write session events.
129///
130/// This is a userver-native analogue of YDB SDK
131/// `ISimpleBlockingWriteSession`: methods may wait for YDB flow-control
132/// continuation tokens, but waiting suspends the current coroutine instead of
133/// blocking an OS thread. It wraps a single `IWriteSession`; its simple API does
134/// not expose the native event loop or acknowledgments.
135///
136/// @see https://ydb.tech/docs/en/reference/ydb-sdk/topic#write
137class TopicSimpleWriteSession final {
138public:
139 /// @cond
140 /// For internal use only.
141 explicit TopicSimpleWriteSession(std::shared_ptr<NYdb::NTopic::IWriteSession> write_session);
142 /// @endcond
143
144 TopicSimpleWriteSession(const TopicSimpleWriteSession&) = delete;
145 TopicSimpleWriteSession& operator=(const TopicSimpleWriteSession&) = delete;
146 TopicSimpleWriteSession(TopicSimpleWriteSession&&) noexcept;
147 TopicSimpleWriteSession& operator=(TopicSimpleWriteSession&&) noexcept;
148
149 /// @brief Write a single message.
150 ///
151 /// Waits until YDB provides a continuation token or until `deadline`.
152 /// @returns true if the message was enqueued for writing, false if the
153 /// deadline was reached or the session was closed before a token arrived.
154 bool Write(
155 NYdb::NTopic::TWriteMessage&& message,
156 NYdb::TTransactionBase* tx = nullptr,
157 engine::Deadline deadline = {}
158 );
159
160 /// @brief Write a single message using basic message options.
161 bool Write(
162 std::string_view data,
163 std::optional<std::uint64_t> seq_no = std::nullopt,
164 std::optional<std::chrono::system_clock::time_point> create_timestamp = std::nullopt,
165 engine::Deadline deadline = {}
166 );
167
168 /// @brief Wait until initial SeqNo is discovered from the server.
169 std::uint64_t GetInitSeqNo(engine::Deadline deadline = {});
170
171 /// @brief Close the write session.
172 ///
173 /// Waits for all in-flight messages to be acknowledged.
174 /// Force closes after `timeout`.
175 /// @returns `true` if all writes were completed and acknowledged. Returns
176 /// `false` if the timeout expired and some writes were aborted; in that
177 /// case their delivery is not guaranteed and should be handled as an
178 /// application-level delivery failure.
179 bool Close(std::chrono::milliseconds timeout);
180
181 /// @brief Returns true if the write session is alive and active.
182 bool IsAlive() const noexcept;
183
184 /// @brief Get native write session
185 ///
186 /// @warning Use with care! Facilities from @ref userver/drivers/subscribable_futures.hpp can help
187 /// with non-blocking wait operations.
188 NYdb::NTopic::IWriteSession& GetNativeTopicWriteSession() USERVER_IMPL_LIFETIME_BOUND;
189
190private:
191 std::optional<NYdb::NTopic::TContinuationToken> WaitForToken(engine::Deadline deadline);
192
193 std::shared_ptr<NYdb::NTopic::IWriteSession> write_session_;
194 std::atomic_bool closed_{false};
195};
196
197/// @brief Settings for TopicProducer.
198class TopicProducerSettings final : public NYdb::NTopic::TProducerSettings {
199 using NYdb::NTopic::TProducerSettings::MaxBlockTimeout;
200 using NYdb::NTopic::TProducerSettings::MaxBlockTimeout_;
201 using NYdb::NTopic::TProducerSettings::MaxMemoryUsage;
202 using NYdb::NTopic::TProducerSettings::MaxMemoryUsage_;
203};
204
205/// @brief Native YDB producer used for partition-aware writes to a topic.
206///
207/// Unlike TopicSimpleWriteSession, this is a wrapper around the YDB SDK
208/// `IProducer`. It selects partitions by message key or explicit partition and
209/// manages the corresponding write sessions internally.
210///
211/// Write() fails immediately if the internal buffer is overloaded.
212///
213/// @see https://ydb.tech/docs/en/reference/ydb-sdk/topic#write
214class TopicProducer final {
215public:
216 /// @cond
217 /// For internal use only.
218 explicit TopicProducer(std::shared_ptr<NYdb::NTopic::IProducer> producer);
219 /// @endcond
220
221 /// @brief Write a single message to the topic.
222 ///
223 /// Adds the message to the internal buffer and returns its queueing status.
224 /// Fails immediately with `EWriteStatus::Timeout` if the buffer is full.
225 /// Use Flush() to wait for the buffered messages to be persistently written.
226 NYdb::NTopic::TWriteResult Write(NYdb::NTopic::TWriteMessage&& message);
227
228 /// @brief Flush all buffered messages to the server.
229 ///
230 /// Waits until all in-flight messages are acknowledged.
231 /// @param deadline timeout for flush completion
232 NYdb::NTopic::TFlushResult Flush(engine::Deadline deadline = {});
233
234 /// @brief Close the producer.
235 ///
236 /// Waits for all in-flight messages to be acknowledged.
237 /// Force closes after timeout.
238 NYdb::NTopic::TCloseResult Close(std::chrono::milliseconds timeout);
239
240 /// @brief Get native producer.
241 ///
242 /// @warning Use with care! Facilities from @ref userver/drivers/subscribable_futures.hpp can help
243 /// with non-blocking wait operations.
244 NYdb::NTopic::IProducer& GetNativeTopicProducer() USERVER_IMPL_LIFETIME_BOUND;
245
246private:
247 std::shared_ptr<NYdb::NTopic::IProducer> producer_;
248};
249
250/// @ingroup userver_clients
251///
252/// @brief YDB Topic Client
253///
254/// @see https://ydb.tech/docs/en/concepts/topic
255class TopicClient final {
256public:
257 static constexpr std::uint64_t kDefaultProducerMaxMemoryUsageBytes = 2ULL * 1024 * 1024 * 1024;
258
259 /// @cond
260 // For internal use only.
261 TopicClient(std::shared_ptr<impl::Driver> driver, impl::TopicSettings settings);
262 /// @endcond
263
264 ~TopicClient();
265
266 /// Alter topic
267 void AlterTopic(const std::string& path, const NYdb::NTopic::TAlterTopicSettings& settings);
268
269 /// Describe topic
270 NYdb::NTopic::TDescribeTopicResult DescribeTopic(const std::string& path);
271
272 /// Create read session
273 TopicReadSession CreateReadSession(const NYdb::NTopic::TReadSessionSettings& settings);
274
275 /// Create write session
276 TopicWriteSession CreateWriteSession(const NYdb::NTopic::TWriteSessionSettings& settings);
277
278 /// Create simple write session.
279 ///
280 /// @note Event handlers from `settings` are not compatible with
281 /// TopicSimpleWriteSession. They are reset with a warning.
282 TopicSimpleWriteSession CreateSimpleWriteSession(const NYdb::NTopic::TWriteSessionSettings& settings);
283
284 /// Create producer.
285 ///
286 /// Unlike TopicSimpleWriteSession, TopicProducer is a native YDB
287 /// multi-session producer: it routes each message by key or explicit
288 /// partition and fails immediately if its buffer is overloaded. Flush()
289 /// waits for persistence without closing the producer;
290 /// TopicSimpleWriteSession instead flushes pending writes as part of
291 /// Close().
292 /// @param max_memory_usage_bytes maximum buffered message memory in bytes
293 TopicProducer CreateProducer(
294 const TopicProducerSettings& settings,
295 std::uint64_t max_memory_usage_bytes = kDefaultProducerMaxMemoryUsageBytes
296 );
297
298 /// Get native topic client
299 /// @warning Use with care! Facilities from
300 /// `<core/include/userver/drivers/subscribable_futures.hpp>` can help with
301 /// non-blocking wait operations.
302 NYdb::NTopic::TTopicClient& GetNativeTopicClient() USERVER_IMPL_LIFETIME_BOUND;
303
304private:
305 std::shared_ptr<impl::Driver> driver_;
306 // Owned executors: Stop() only after `topic_client_` is destroyed (see
307 // ~TopicClient). Joining these threads after the native client is gone
308 // avoids atexit use-after-destroy (e.g. SEGV in TCodecMap). Stopping them
309 // while TTopicClient is still alive would deadlock or stall writes.
310 NYdb::IExecutor::TPtr compression_executor_;
311 NYdb::IExecutor::TPtr handlers_executor_;
312 // `reset()` in ~TopicClient runs before Stop() on the executors above.
313 std::optional<NYdb::NTopic::TTopicClient> topic_client_;
314};
315
316} // namespace ydb
317
318USERVER_NAMESPACE_END