userver: userver/storages/redis/reply.hpp Source File
Loading...
Searching...
No Matches
reply.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/redis/reply.hpp
4/// @brief Redis reply payload (ReplyData) and per-command reply wrapper (Reply)
5
6#include <string>
7#include <variant>
8#include <vector>
9
10#include <userver/logging/log_extra.hpp>
11#include <userver/utils/assert.hpp>
12
13#include <userver/storages/redis/base.hpp>
14#include <userver/storages/redis/impl/deadline_propagation_meta.hpp>
15#include <userver/storages/redis/reply_fwd.hpp>
16#include <userver/storages/redis/reply_status.hpp>
17
18struct redisReply;
19
20USERVER_NAMESPACE_BEGIN
21
22namespace storages::redis {
23
24/// @brief Typed view of a hiredis `redisReply` value
25class ReplyData final {
26public:
27 using Array = std::vector<ReplyData>;
28
29 enum class Type {
30 kNoReply,
31 kString,
32 kArray,
33 kInteger,
34 kNil,
35 kStatus,
36 kError,
37 };
38
39 class MovableKeyValues final {
40 public:
41 class View final {
42 public:
43 View(ReplyData& key_data, ReplyData& value_data) noexcept : key_data_(key_data), value_data_(value_data) {}
44
45 std::string& Key() noexcept { return key_data_.GetString(); }
46 std::string& Value() noexcept { return value_data_.GetString(); }
47
48 private:
49 ReplyData& key_data_;
50 ReplyData& value_data_;
51 };
52
53 class Iterator final {
54 public:
55 constexpr Iterator(Array& array, std::size_t index) noexcept : array_(array), index_(index) {}
56 Iterator& operator++() noexcept {
57 ++index_;
58 return *this;
59 }
60 bool operator!=(const Iterator& r) const noexcept { return index_ != r.index_; }
61 View operator*() noexcept { return {array_[index_ * 2], array_[index_ * 2 + 1]}; }
62
63 private:
64 Array& array_;
65 std::size_t index_;
66 };
67
68 explicit MovableKeyValues(Array& array) noexcept : array_(array) {}
69
70 Iterator begin() const noexcept { return {array_, 0}; }
71 Iterator end() const noexcept { return {array_, size()}; }
72
73 std::size_t size() const noexcept { return array_.size() / 2; }
74
75 private:
76 Array& array_;
77 };
78
79 MovableKeyValues GetMovableKeyValues();
80
81 ReplyData(const redisReply* reply);
82 ReplyData(Array&& array);
83 ReplyData(std::string s);
84 ReplyData(int value);
85 static ReplyData CreateError(std::string&& error_msg);
86 static ReplyData CreateStatus(std::string&& status_msg);
87 static ReplyData CreateNil();
88
89 explicit operator bool() const noexcept { return GetType() != Type::kNoReply; }
90
91 Type GetType() const noexcept {
92 UASSERT(!data_.valueless_by_exception());
93 return Type(data_.index());
94 }
95 std::string GetTypeString() const;
96
97 inline bool IsString() const noexcept { return GetType() == Type::kString; }
98 inline bool IsArray() const noexcept { return GetType() == Type::kArray; }
99 inline bool IsInt() const noexcept { return GetType() == Type::kInteger; }
100 inline bool IsNil() const noexcept { return GetType() == Type::kNil; }
101 inline bool IsStatus() const noexcept { return GetType() == Type::kStatus; }
102 inline bool IsError() const noexcept { return GetType() == Type::kError; }
103 bool IsUnusableInstanceError() const;
104 bool IsReadonlyError() const;
105 bool IsUnknownCommandError() const;
106
107 bool IsErrorMoved() const { return IsError() && !GetError().compare(0, 6, "MOVED "); }
108
109 bool IsErrorAsk() const { return IsError() && !GetError().compare(0, 4, "ASK "); }
110
111 bool IsErrorClusterdown() const { return IsError() && !GetError().compare(0, 12, "CLUSTERDOWN "); }
112
113 const std::string& GetString() const {
114 UASSERT(IsString());
115 return std::get_if<String>(&data_)->GetUnderlying();
116 }
117
118 std::string& GetString() {
119 UASSERT(IsString());
120 return std::get_if<String>(&data_)->GetUnderlying();
121 }
122
123 const Array& GetArray() const {
124 UASSERT(IsArray());
125 return *std::get_if<Array>(&data_);
126 }
127
128 Array& GetArray() {
129 UASSERT(IsArray());
130 return *std::get_if<Array>(&data_);
131 }
132
133 std::int64_t GetInt() const {
134 UASSERT(IsInt());
135 return *std::get_if<Integer>(&data_);
136 }
137
138 const std::string& GetStatus() const {
139 UASSERT(IsStatus());
140 return std::get_if<Status>(&data_)->GetUnderlying();
141 }
142
143 std::string& GetStatus() {
144 UASSERT(IsStatus());
145 return std::get_if<Status>(&data_)->GetUnderlying();
146 }
147
148 const std::string& GetError() const {
149 UASSERT(IsError());
150 return std::get_if<Error>(&data_)->GetUnderlying();
151 }
152
153 std::string& GetError() {
154 UASSERT(IsError());
155 return std::get_if<Error>(&data_)->GetUnderlying();
156 }
157
158 std::size_t GetSize() const noexcept;
159
160 std::string ToDebugString() const;
161 static std::string TypeToString(Type type);
162
163 void ExpectType(ReplyData::Type type, const std::string& request_description = {}) const;
164
165 void ExpectString(const std::string& request_description = {}) const;
166 void ExpectArray(const std::string& request_description = {}) const;
167 void ExpectInt(const std::string& request_description = {}) const;
168 void ExpectNil(const std::string& request_description = {}) const;
169 void ExpectStatus(const std::string& request_description = {}) const;
170 void ExpectStatusEqualTo(const std::string& expected_status_str, const std::string& request_description = {}) const;
171 void ExpectError(const std::string& request_description = {}) const;
172
173private:
174 ReplyData() = default;
175
176 [[noreturn]] void ThrowUnexpectedReplyType(ReplyData::Type expected, const std::string& request_description) const;
177
178 struct NoReply {};
179 using String = utils::StrongTypedef<class StringTag, std::string>;
180 using Integer = std::int64_t;
181 struct Nil {};
182 using Status = utils::StrongTypedef<class StatusTag, std::string>;
183 using Error = utils::StrongTypedef<class ErrorTag, std::string>;
184
185 // Matches `enum class Type`
186 std::variant<NoReply, String, Array, Integer, Nil, Status, Error> data_{};
187};
188
189/// @brief Redis command reply with metadata (server, status, timing)
190class Reply final {
191public:
192 Reply(std::string command, ReplyData&& reply_data, ReplyStatus reply_status = ReplyStatus::kOk);
193
194 std::string server;
195 ServerId server_id;
196 const std::string cmd;
197 ReplyData data;
198 const ReplyStatus status;
199 double time = 0.0;
200 logging::LogExtra log_extra;
201 /// @cond For internal use only
202 DeadlinePropagationMeta deadline_propagation_meta;
203
204 operator bool() const { return IsOk(); }
205
206 std::string_view GetStatusString() const {
207 return (!IsOk() && data.IsError() ? data.GetError() : std::string_view{});
208 }
209
210 bool IsOk() const;
211 bool IsLoggableError() const;
212 bool IsUnusableInstanceError() const;
213 bool IsReadonlyError() const;
214 bool IsUnknownCommandError() const;
215 const logging::LogExtra& GetLogExtra() const;
216 void FillSpanTags(tracing::Span& span) const;
217
218 void ExpectIsOk(const std::string& request_description = {}) const;
219
220 const std::string& GetRequestDescription(const std::string& request_description) const;
221};
222
223} // namespace storages::redis
224
225USERVER_NAMESPACE_END