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