|
12 | 12 | #include <ydb/library/actors/core/actorsystem.h>
|
13 | 13 | #include <ydb/library/actors/core/event_local.h>
|
14 | 14 | #include <ydb/library/actors/core/events.h>
|
| 15 | +#include <ydb/library/actors/core/hfunc.h> |
| 16 | +#include <library/cpp/retry/retry_policy.h> |
15 | 17 | #include <library/cpp/threading/future/future.h>
|
16 | 18 |
|
17 | 19 | namespace NKikimr {
|
18 | 20 |
|
19 |
| -// TODO: add retry logic |
20 | 21 | class TQueryBase : public NActors::TActorBootstrapped<TQueryBase> {
|
21 | 22 | protected:
|
22 | 23 | struct TTxControl {
|
@@ -168,4 +169,101 @@ class TQueryBase : public NActors::TActorBootstrapped<TQueryBase> {
|
168 | 169 | std::vector<NYdb::TResultSet> ResultSets;
|
169 | 170 | };
|
170 | 171 |
|
| 172 | +template<typename TQueryActor, typename TResponse, typename ...TArgs> |
| 173 | +class TQueryRetryActor : public NActors::TActorBootstrapped<TQueryRetryActor<TQueryActor, TResponse, TArgs...>> { |
| 174 | +public: |
| 175 | + using TBase = NActors::TActorBootstrapped<TQueryRetryActor<TQueryActor, TResponse, TArgs...>>; |
| 176 | + using IRetryPolicy = IRetryPolicy<Ydb::StatusIds::StatusCode>; |
| 177 | + |
| 178 | + explicit TQueryRetryActor(const NActors::TActorId& replyActorId, const TArgs&... args, TDuration maxRetryTime = TDuration::Seconds(1)) |
| 179 | + : ReplyActorId(replyActorId) |
| 180 | + , RetryPolicy(IRetryPolicy::GetExponentialBackoffPolicy( |
| 181 | + Retryable, TDuration::MilliSeconds(10), |
| 182 | + TDuration::MilliSeconds(200), TDuration::Seconds(1), |
| 183 | + std::numeric_limits<size_t>::max(), maxRetryTime |
| 184 | + )) |
| 185 | + , CreateQueryActor([=]() { |
| 186 | + return new TQueryActor(args...); |
| 187 | + }) |
| 188 | + {} |
| 189 | + |
| 190 | + TQueryRetryActor(const NActors::TActorId& replyActorId, IRetryPolicy::TPtr retryPolicy, const TArgs&... args) |
| 191 | + : ReplyActorId(replyActorId) |
| 192 | + , RetryPolicy(retryPolicy) |
| 193 | + , CreateQueryActor([=]() { |
| 194 | + return new TQueryActor(args...); |
| 195 | + }) |
| 196 | + {} |
| 197 | + |
| 198 | + void StartQueryActor() const { |
| 199 | + TBase::Register(CreateQueryActor()); |
| 200 | + } |
| 201 | + |
| 202 | + void Bootstrap() { |
| 203 | + TBase::Become(&TQueryRetryActor::StateFunc); |
| 204 | + StartQueryActor(); |
| 205 | + } |
| 206 | + |
| 207 | + STRICT_STFUNC(StateFunc, |
| 208 | + hFunc(NActors::TEvents::TEvWakeup, Wakeup); |
| 209 | + hFunc(TResponse, Handle); |
| 210 | + ) |
| 211 | + |
| 212 | + void Wakeup(NActors::TEvents::TEvWakeup::TPtr&) { |
| 213 | + StartQueryActor(); |
| 214 | + } |
| 215 | + |
| 216 | + void Handle(const typename TResponse::TPtr& ev) { |
| 217 | + const Ydb::StatusIds::StatusCode status = ev->Get()->Status; |
| 218 | + if (Retryable(status) == ERetryErrorClass::NoRetry) { |
| 219 | + Reply(ev); |
| 220 | + return; |
| 221 | + } |
| 222 | + |
| 223 | + if (RetryState == nullptr) { |
| 224 | + RetryState = RetryPolicy->CreateRetryState(); |
| 225 | + } |
| 226 | + |
| 227 | + if (auto delay = RetryState->GetNextRetryDelay(status)) { |
| 228 | + TBase::Schedule(*delay, new NActors::TEvents::TEvWakeup()); |
| 229 | + } else { |
| 230 | + Reply(ev); |
| 231 | + } |
| 232 | + } |
| 233 | + |
| 234 | + void Reply(const typename TResponse::TPtr& ev) { |
| 235 | + TBase::Send(ev->Forward(ReplyActorId)); |
| 236 | + TBase::PassAway(); |
| 237 | + } |
| 238 | + |
| 239 | + static ERetryErrorClass Retryable(Ydb::StatusIds::StatusCode status) { |
| 240 | + if (status == Ydb::StatusIds::SUCCESS) { |
| 241 | + return ERetryErrorClass::NoRetry; |
| 242 | + } |
| 243 | + |
| 244 | + if (status == Ydb::StatusIds::INTERNAL_ERROR |
| 245 | + || status == Ydb::StatusIds::UNAVAILABLE |
| 246 | + || status == Ydb::StatusIds::BAD_SESSION |
| 247 | + || status == Ydb::StatusIds::SESSION_EXPIRED |
| 248 | + || status == Ydb::StatusIds::SESSION_BUSY |
| 249 | + || status == Ydb::StatusIds::TIMEOUT |
| 250 | + || status == Ydb::StatusIds::ABORTED) { |
| 251 | + return ERetryErrorClass::ShortRetry; |
| 252 | + } |
| 253 | + |
| 254 | + if (status == Ydb::StatusIds::OVERLOADED |
| 255 | + || status == Ydb::StatusIds::UNDETERMINED) { |
| 256 | + return ERetryErrorClass::LongRetry; |
| 257 | + } |
| 258 | + |
| 259 | + return ERetryErrorClass::NoRetry; |
| 260 | + } |
| 261 | + |
| 262 | +private: |
| 263 | + const NActors::TActorId ReplyActorId; |
| 264 | + const IRetryPolicy::TPtr RetryPolicy; |
| 265 | + const std::function<TQueryActor*()> CreateQueryActor; |
| 266 | + IRetryPolicy::IRetryState::TPtr RetryState = nullptr; |
| 267 | +}; |
| 268 | + |
171 | 269 | } // namespace NKikimr
|
0 commit comments