Skip to content

Commit eeb2175

Browse files
authored
Use 1-thread pool executor for subsession event handlers (#6457)
1 parent 4ee0082 commit eeb2175

File tree

5 files changed

+60
-55
lines changed

5 files changed

+60
-55
lines changed

ydb/public/sdk/cpp/client/ydb_federated_topic/impl/federated_topic_impl.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ TFederatedTopicClient::TImpl::CreateWriteSession(const TFederatedWriteSessionSet
3535
splitSettings.EventHandlers_.HandlersExecutor(ClientSettings.DefaultHandlersExecutor_);
3636
}
3737
}
38-
auto session = std::make_shared<TFederatedWriteSession>(splitSettings, Connections, ClientSettings, GetObserver(), ProvidedCodecs);
38+
auto session = std::make_shared<TFederatedWriteSession>(
39+
splitSettings, Connections, ClientSettings, GetObserver(), ProvidedCodecs, GetSubsessionHandlersExecutor());
3940
session->Start();
4041
return std::move(session);
4142
}
@@ -49,4 +50,13 @@ void TFederatedTopicClient::TImpl::InitObserver() {
4950
}
5051
}
5152

53+
auto TFederatedTopicClient::TImpl::GetSubsessionHandlersExecutor() -> NTopic::IExecutor::TPtr {
54+
with_lock (Lock) {
55+
if (!SubsessionHandlersExecutor) {
56+
SubsessionHandlersExecutor = NTopic::CreateThreadPoolExecutor(1);
57+
}
58+
return SubsessionHandlersExecutor;
59+
}
60+
}
61+
5262
}

ydb/public/sdk/cpp/client/ydb_federated_topic/impl/federated_topic_impl.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,20 @@ class TFederatedTopicClient::TImpl {
7474

7575
void InitObserver();
7676

77+
private:
78+
79+
// Use single-threaded executor to prevent deadlocks inside subsession event handlers.
80+
NTopic::IExecutor::TPtr GetSubsessionHandlersExecutor();
81+
7782
private:
7883
std::shared_ptr<TGRpcConnectionsImpl> Connections;
7984
const TFederatedTopicClientSettings ClientSettings;
8085
std::shared_ptr<TFederatedDbObserver> Observer;
8186
std::shared_ptr<std::unordered_map<NTopic::ECodec, THolder<NTopic::ICodec>>> ProvidedCodecs =
8287
std::make_shared<std::unordered_map<NTopic::ECodec, THolder<NTopic::ICodec>>>();
88+
89+
NTopic::IExecutor::TPtr SubsessionHandlersExecutor;
90+
8391
TAdaptiveLock Lock;
8492
};
8593

ydb/public/sdk/cpp/client/ydb_federated_topic/impl/federated_write_session.cpp

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@ TFederatedWriteSessionImpl::TFederatedWriteSessionImpl(
2929
std::shared_ptr<TGRpcConnectionsImpl> connections,
3030
const TFederatedTopicClientSettings& clientSettings,
3131
std::shared_ptr<TFederatedDbObserver> observer,
32-
std::shared_ptr<std::unordered_map<NTopic::ECodec, THolder<NTopic::ICodec>>> codecs
32+
std::shared_ptr<std::unordered_map<NTopic::ECodec, THolder<NTopic::ICodec>>> codecs,
33+
NTopic::IExecutor::TPtr subsessionHandlersExecutor
3334
)
3435
: Settings(settings)
3536
, Connections(std::move(connections))
3637
, SubclientSettings(FromFederated(clientSettings))
3738
, ProvidedCodecs(std::move(codecs))
39+
, SubsessionHandlersExecutor(subsessionHandlersExecutor)
3840
, Observer(std::move(observer))
3941
, AsyncInit(Observer->WaitForFirstState())
4042
, FederationState(nullptr)
@@ -71,15 +73,16 @@ void TFederatedWriteSessionImpl::IssueTokenIfAllowed() {
7173
}
7274
}
7375

74-
void TFederatedWriteSessionImpl::UpdateFederationStateImpl() {
76+
std::shared_ptr<NTopic::IWriteSession> TFederatedWriteSessionImpl::UpdateFederationStateImpl() {
7577
Y_ABORT_UNLESS(Lock.IsLocked());
7678
// Even after the user has called the Close method, transitioning the session to the CLOSING state,
7779
// we keep updating the federation state, as the session may still have some messages to send in its queues,
7880
// and for that we need to know the current state of the federation.
7981
if (SessionState < State::CLOSED) {
8082
FederationState = Observer->GetState();
81-
OnFederationStateUpdateImpl();
83+
return OnFederationStateUpdateImpl();
8284
}
85+
return {};
8386
}
8487

8588
void TFederatedWriteSessionImpl::Start() {
@@ -104,15 +107,16 @@ void TFederatedWriteSessionImpl::Start() {
104107
});
105108
}
106109

107-
void TFederatedWriteSessionImpl::OpenSubsessionImpl(std::shared_ptr<TDbInfo> db) {
110+
std::shared_ptr<NTopic::IWriteSession> TFederatedWriteSessionImpl::OpenSubsessionImpl(std::shared_ptr<TDbInfo> db) {
108111
Y_ABORT_UNLESS(Lock.IsLocked());
109112

110113
++SubsessionGeneration;
111114

115+
std::shared_ptr<NTopic::IWriteSession> oldSubsession;
116+
112117
if (Subsession) {
113118
PendingToken.Clear();
114-
OldSubsession = std::move(Subsession);
115-
OldSubsession->Close(TDuration::Zero());
119+
std::swap(oldSubsession, Subsession);
116120
}
117121

118122
auto clientSettings = SubclientSettings;
@@ -122,22 +126,18 @@ void TFederatedWriteSessionImpl::OpenSubsessionImpl(std::shared_ptr<TDbInfo> db)
122126
auto subclient = make_shared<NTopic::TTopicClient::TImpl>(Connections, clientSettings);
123127

124128
auto handlers = NTopic::TWriteSessionSettings::TEventHandlers()
125-
.HandlersExecutor(Settings.EventHandlers_.HandlersExecutor_)
129+
.HandlersExecutor(SubsessionHandlersExecutor)
126130
.ReadyToAcceptHandler([selfCtx = SelfContext, generation = SubsessionGeneration](NTopic::TWriteSessionEvent::TReadyToAcceptEvent& ev) {
127131
if (auto self = selfCtx->LockShared()) {
128-
TDeferredWrite deferred;
129-
130132
with_lock(self->Lock) {
131133
if (generation != self->SubsessionGeneration) {
132134
return;
133135
}
134136

135137
Y_ABORT_UNLESS(self->PendingToken.Empty());
136138
self->PendingToken = std::move(ev.ContinuationToken);
137-
self->PrepareDeferredWriteImpl(deferred);
139+
self->MaybeWriteImpl();
138140
}
139-
140-
deferred.DoWrite();
141141
}
142142
})
143143
.AcksHandler([selfCtx = SelfContext](NTopic::TWriteSessionEvent::TAcksEvent& ev) {
@@ -182,6 +182,8 @@ void TFederatedWriteSessionImpl::OpenSubsessionImpl(std::shared_ptr<TDbInfo> db)
182182

183183
Subsession = subclient->CreateWriteSession(wsSettings);
184184
CurrentDatabase = db;
185+
186+
return oldSubsession;
185187
}
186188

187189
std::pair<std::shared_ptr<TDbInfo>, EStatus> SelectDatabaseByHashImpl(
@@ -267,13 +269,13 @@ std::pair<std::shared_ptr<TDbInfo>, EStatus> SelectDatabaseImpl(
267269
return SelectDatabaseByHashImpl(settings, dbInfos);
268270
}
269271

270-
void TFederatedWriteSessionImpl::OnFederationStateUpdateImpl() {
272+
std::shared_ptr<NTopic::IWriteSession> TFederatedWriteSessionImpl::OnFederationStateUpdateImpl() {
271273
Y_ABORT_UNLESS(Lock.IsLocked());
272274
if (!FederationState->Status.IsSuccess()) {
273275
// The observer became stale, it won't try to get federation state anymore due to retry policy,
274276
// so there's no reason to keep the write session alive.
275277
CloseImpl(FederationState->Status.GetStatus(), NYql::TIssues(FederationState->Status.GetIssues()));
276-
return;
278+
return {};
277279
}
278280

279281
Y_ABORT_UNLESS(!FederationState->DbInfos.empty());
@@ -292,19 +294,22 @@ void TFederatedWriteSessionImpl::OnFederationStateUpdateImpl() {
292294
LOG_LAZY(Log, TLOG_ERR, GetLogPrefixImpl() << message << ". Status: " << status);
293295
CloseImpl(status, NYql::TIssues{NYql::TIssue(message)});
294296
}
295-
return;
297+
return {};
296298
}
297299
RetryState.reset();
298300

301+
std::shared_ptr<NTopic::IWriteSession> oldSubsession;
299302
if (!DatabasesAreSame(preferrableDb, CurrentDatabase)) {
300303
LOG_LAZY(Log, TLOG_INFO, GetLogPrefixImpl()
301304
<< "Start federated write session to database '" << preferrableDb->name()
302305
<< "' (previous was " << (CurrentDatabase ? CurrentDatabase->name() : "<empty>") << ")"
303306
<< " FederationState: " << *FederationState);
304-
OpenSubsessionImpl(preferrableDb);
307+
oldSubsession = OpenSubsessionImpl(preferrableDb);
305308
}
306309

307310
ScheduleFederationStateUpdateImpl(UPDATE_FEDERATION_STATE_DELAY);
311+
312+
return oldSubsession;
308313
}
309314

310315
void TFederatedWriteSessionImpl::ScheduleFederationStateUpdateImpl(TDuration delay) {
@@ -314,8 +319,10 @@ void TFederatedWriteSessionImpl::ScheduleFederationStateUpdateImpl(TDuration del
314319
if (auto self = selfCtx->LockShared()) {
315320
std::shared_ptr<NTopic::IWriteSession> old;
316321
with_lock(self->Lock) {
317-
self->UpdateFederationStateImpl();
318-
old = std::move(self->OldSubsession);
322+
old = self->UpdateFederationStateImpl();
323+
}
324+
if (old) {
325+
old->Close(TDuration::Zero());
319326
}
320327
}
321328
}
@@ -378,24 +385,20 @@ void TFederatedWriteSessionImpl::WriteEncoded(NTopic::TContinuationToken&& token
378385
}
379386

380387
void TFederatedWriteSessionImpl::WriteInternal(NTopic::TContinuationToken&&, TWrappedWriteMessage&& wrapped) {
381-
TDeferredWrite deferred(Subsession);
382-
383388
with_lock(Lock) {
384389
ClientHasToken = false;
385390
if (!wrapped.Message.CreateTimestamp_.Defined()) {
386391
wrapped.Message.CreateTimestamp_ = TInstant::Now();
387392
}
388393
BufferFreeSpace -= wrapped.Message.Data.size();
389394
OriginalMessagesToPassDown.emplace_back(std::move(wrapped));
390-
PrepareDeferredWriteImpl(deferred);
395+
MaybeWriteImpl();
391396
}
392397

393-
deferred.DoWrite();
394-
395398
IssueTokenIfAllowed();
396399
}
397400

398-
bool TFederatedWriteSessionImpl::PrepareDeferredWriteImpl(TDeferredWrite& deferred) {
401+
bool TFederatedWriteSessionImpl::MaybeWriteImpl() {
399402
Y_ABORT_UNLESS(Lock.IsLocked());
400403
if (PendingToken.Empty()) {
401404
return false;
@@ -405,9 +408,7 @@ bool TFederatedWriteSessionImpl::PrepareDeferredWriteImpl(TDeferredWrite& deferr
405408
}
406409
OriginalMessagesToGetAck.push_back(std::move(OriginalMessagesToPassDown.front()));
407410
OriginalMessagesToPassDown.pop_front();
408-
deferred.Writer = Subsession;
409-
deferred.Token.ConstructInPlace(std::move(*PendingToken));
410-
deferred.Message.ConstructInPlace(std::move(OriginalMessagesToGetAck.back().Message));
411+
Subsession->Write(std::move(*PendingToken), std::move(OriginalMessagesToGetAck.back().Message));
411412
PendingToken.Clear();
412413
return true;
413414
}

ydb/public/sdk/cpp/client/ydb_federated_topic/impl/federated_write_session.h

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ class TFederatedWriteSessionImpl : public NTopic::TContinuationTokenIssuer,
2727
std::shared_ptr<TGRpcConnectionsImpl> connections,
2828
const TFederatedTopicClientSettings& clientSetttings,
2929
std::shared_ptr<TFederatedDbObserver> observer,
30-
std::shared_ptr<std::unordered_map<NTopic::ECodec, THolder<NTopic::ICodec>>> codecs);
30+
std::shared_ptr<std::unordered_map<NTopic::ECodec, THolder<NTopic::ICodec>>> codecs,
31+
NTopic::IExecutor::TPtr subsessionHandlersExecutor);
3132

3233
~TFederatedWriteSessionImpl() = default;
3334

@@ -62,40 +63,22 @@ class TFederatedWriteSessionImpl : public NTopic::TContinuationTokenIssuer,
6263
}
6364
};
6465

65-
struct TDeferredWrite {
66-
TDeferredWrite() {}
67-
explicit TDeferredWrite(std::shared_ptr<NTopic::IWriteSession> writer)
68-
: Writer(std::move(writer)) {
69-
}
70-
71-
void DoWrite() {
72-
if (Token.Empty() && Message.Empty()) {
73-
return;
74-
}
75-
Y_ABORT_UNLESS(Token.Defined() && Message.Defined());
76-
return Writer->Write(std::move(*Token), std::move(*Message));
77-
}
78-
79-
std::shared_ptr<NTopic::IWriteSession> Writer;
80-
TMaybe<NTopic::TContinuationToken> Token;
81-
TMaybe<NTopic::TWriteMessage> Message;
82-
};
83-
8466
private:
8567
void Start();
86-
void OpenSubsessionImpl(std::shared_ptr<TDbInfo> db);
8768

88-
void OnFederationStateUpdateImpl();
69+
std::shared_ptr<NTopic::IWriteSession> OpenSubsessionImpl(std::shared_ptr<TDbInfo> db);
70+
std::shared_ptr<NTopic::IWriteSession> UpdateFederationStateImpl();
71+
std::shared_ptr<NTopic::IWriteSession> OnFederationStateUpdateImpl();
72+
8973
void ScheduleFederationStateUpdateImpl(TDuration delay);
9074

9175
void WriteInternal(NTopic::TContinuationToken&&, TWrappedWriteMessage&& message);
92-
bool PrepareDeferredWriteImpl(TDeferredWrite& deferred);
76+
bool MaybeWriteImpl();
9377

9478
void CloseImpl(EStatus statusCode, NYql::TIssues&& issues);
9579
void CloseImpl(NTopic::TSessionClosedEvent const& ev);
9680

9781
bool MessageQueuesAreEmptyImpl() const;
98-
void UpdateFederationStateImpl();
9982

10083
void IssueTokenIfAllowed();
10184

@@ -107,6 +90,7 @@ class TFederatedWriteSessionImpl : public NTopic::TContinuationTokenIssuer,
10790
std::shared_ptr<TGRpcConnectionsImpl> Connections;
10891
const NTopic::TTopicClientSettings SubclientSettings;
10992
std::shared_ptr<std::unordered_map<NTopic::ECodec, THolder<NTopic::ICodec>>> ProvidedCodecs;
93+
NTopic::IExecutor::TPtr SubsessionHandlersExecutor;
11094

11195
NTopic::IRetryPolicy::IRetryState::TPtr RetryState;
11296
std::shared_ptr<TFederatedDbObserver> Observer;
@@ -125,7 +109,6 @@ class TFederatedWriteSessionImpl : public NTopic::TContinuationTokenIssuer,
125109

126110
size_t SubsessionGeneration = 0;
127111
std::shared_ptr<NTopic::IWriteSession> Subsession;
128-
std::shared_ptr<NTopic::IWriteSession> OldSubsession;
129112

130113
std::shared_ptr<NTopic::TWriteSessionEventsQueue> ClientEventsQueue;
131114

@@ -156,8 +139,9 @@ class TFederatedWriteSession : public NTopic::IWriteSession,
156139
std::shared_ptr<TGRpcConnectionsImpl> connections,
157140
const TFederatedTopicClientSettings& clientSettings,
158141
std::shared_ptr<TFederatedDbObserver> observer,
159-
std::shared_ptr<std::unordered_map<NTopic::ECodec, THolder<NTopic::ICodec>>> codecs)
160-
: TContextOwner(settings, std::move(connections), clientSettings, std::move(observer), codecs) {}
142+
std::shared_ptr<std::unordered_map<NTopic::ECodec, THolder<NTopic::ICodec>>> codecs,
143+
NTopic::IExecutor::TPtr subsessionHandlersExecutor)
144+
: TContextOwner(settings, std::move(connections), clientSettings, std::move(observer), codecs, subsessionHandlersExecutor) {}
161145

162146
NThreading::TFuture<void> WaitEvent() override {
163147
return TryGetImpl()->WaitEvent();

ydb/public/sdk/cpp/client/ydb_federated_topic/ut/basic_usage_ut.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,8 @@ Y_UNIT_TEST_SUITE(BasicUsage) {
10961096
++(ev.IsSuccess() ? successfulSessionClosedEvents : otherSessionClosedEvents);
10971097
});
10981098

1099+
writeSettings.EventHandlers_.HandlersExecutor(NTopic::CreateSyncExecutor());
1100+
10991101
auto WriteSession = topicClient.CreateWriteSession(writeSettings);
11001102

11011103
TMaybe<NTopic::TContinuationToken> token;

0 commit comments

Comments
 (0)