Skip to content

Commit 09969cd

Browse files
committed
MB-54267: Add extras to RangeScan continue responses
As the value uses a different encoding depending on the scan type it is useful to tag the responses so the value can be decoded without knowing the exact configuration of the scan. Change-Id: Ifc1f2782ba2b6d964fab3431e935d88829ee0a14 Reviewed-on: https://review.couchbase.org/c/kv_engine/+/182205 Tested-by: Build Bot <[email protected]> Reviewed-by: Dave Rigby <[email protected]>
1 parent da39925 commit 09969cd

File tree

14 files changed

+145
-47
lines changed

14 files changed

+145
-47
lines changed

daemon/cookie.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,10 +1220,12 @@ bool Cookie::checkThrottle(size_t pendingRBytes, size_t pendingWBytes) {
12201220
*this, true, pendingRBytes + pendingWBytes);
12211221
}
12221222

1223-
bool Cookie::sendResponse(cb::engine_errc status, std::string_view value) {
1223+
bool Cookie::sendResponse(cb::engine_errc status,
1224+
std::string_view extras,
1225+
std::string_view value) {
12241226
return connection.sendResponse(*this,
12251227
cb::mcbp::to_status(status),
1226-
{},
1228+
extras,
12271229
{},
12281230
value,
12291231
uint8_t(cb::mcbp::Datatype::Raw),

daemon/cookie.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,10 @@ class Cookie : public CookieIface {
648648

649649
/// Cookie implementation checks the bucket throttle
650650
bool checkThrottle(size_t pendingRBytes, size_t pendingWBytes) override;
651-
bool sendResponse(cb::engine_errc status, std::string_view value) override;
651+
/// Cookie implementation calls through to Connection::sendResponse
652+
bool sendResponse(cb::engine_errc status,
653+
std::string_view extras,
654+
std::string_view value) override;
652655

653656
protected:
654657
/**

docs/range_scans/range_scan_continue.md

Lines changed: 63 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -66,54 +66,62 @@ The extras for a continue range scan encodes:
6666
## Response format
6767

6868
A successful continue will trigger the server to iterate forwards through the
69-
range of keys, return keys/documents using a bulk framing.
69+
range of keys and return keys/documents using a bulk framing.
7070

71-
The many keys/documents of a single continue maybe sent (framed) in multiple
72-
responses, where a final response (with bespoke status) indicates the end of the
73-
individual continue or the end of the scan itself. Each response of the continue
74-
may encode multiple keys or documents, the formatting of which is described
75-
in the following sections.
71+
The many keys/documents of a single continue maybe sent using multiple responses.
72+
A final response uses a bespoke range-scan status code to indicate the end of the
73+
individual range-scan-continue or that the scan has completed.
7674

75+
Each response generated by the range-scan-continue may contain a value which
76+
encodes multiple keys or documents, the formatting of which is described
77+
in the following sections. Note that each response will also be given an extras
78+
section, which is defined in a following section.
7779

78-
A successful continue will return key/documents using the following framing. A
79-
series of key/documents always begins with the standard mcbp 24-byte response
80-
header which will include a success status and a value, the value encodes many
81-
keys/documents.
80+
A batch of keys or documents always begins with the standard mcbp 24-byte response
81+
header which will include a "success" status, extras and a value. It is the value
82+
that contains the keys or documents.
8283

83-
The following examples highlight how the responses to range-scan-continue works.
84+
The following examples intend to highlight how the responses from a range-scan-continue work.
8485

85-
For example a range may cover an unknown number of keys and the client only
86+
For example a range covers an unknown number of keys and the client only
8687
wants to handle up to 500 keys per range-scan-continue. Thus the client would
87-
first issue a range-scan-continue (0xDB) with the uuid from range-scan-create
88-
(0xDA), item limit set to 500 and the time limit set to 0.
88+
first issue a range-scan-continue (0xDB) with:
8989

90-
The server will respond with a series of responses, there is no definition of
91-
how many keys each response may encode.
90+
* the uuid from range-scan-create (0xDA)
91+
* item limit set to 500
92+
* time limit set to 0
93+
* byte limit set to 0
9294

93-
Example 1, range scan progresses, yet more continues will be required.
95+
The server will respond with a series of responses. Clients must note that there
96+
is no definition of how many keys or documents each response may include.
97+
98+
Example 1: A continue hits a limit and indicates that another continue is
99+
required because more keys or documents maybe available.
94100

95101
* response status=success, bodylen=261
96102
* response status=success, bodylen=259
97103
* response status=success, bodylen=260
98104
* response status=range-scan-more (0xA6), bodylen=241
99105

100-
In this sequence 500 keys are returned (distributed over 4 different responses).
101-
The final response has the status of range-scan-more informing the client that
102-
the scan has more data.
106+
In this sequence 500 keys are distributed over 4 different responses. The final
107+
response has the status of range-scan-more informing the client that the scan
108+
could have more data.
103109

104-
Example 2, range scan progress to the end
110+
Example 2: A continue now reaches the end of the requested range.
105111

106112
* response status=success, bodylen=262
107113
* response status=range-scan-complete (0xA7), bodylen=120
108114

109-
In this sequence <= 500 keys have been returned (distributed over 2 different
110-
responses). The final response as the status of range-scan-complete, informing
111-
the client that the scan has now completed (server has closed the scan).
115+
In this sequence <= 500 keys have been distributed over 2 different responses.
116+
The status of range-scan-complete informs the client that the scan has now
117+
completed. The server has closed the scan and any further continue will be
118+
rejected.
112119

113-
Example 3, range scan progresses, but vbucket state change interrupts. This
114-
example demonstrates that the client must be aware that the sequence of continue
115-
responses could be bookmarked with a status indicating some other issue. In this
116-
case the range-scan has also been cancelled due to the issue.
120+
Example 3: range scan progresses, but is stopped due to a system state change,
121+
in this case the vbucket is no longer active. This example demonstrates that the
122+
client must be aware that the sequence of continue responses could end with a
123+
status indicating a terminal issue. For these situations the range-scan is
124+
cancelled by the server.
117125

118126
* response status=success, bodylen=261
119127
* response status=success, bodylen=259
@@ -122,6 +130,15 @@ case the range-scan has also been cancelled due to the issue.
122130
In this example < 500 keys have been spread over three responses, the final
123131
response indicates that the target vbucket has changed state.
124132

133+
### Extras Definition
134+
135+
Each response packet includes an extras structure which contains a single 4-byte
136+
"flags" field. This field currently describes the content of the value (keys or
137+
key/meta/value). The field uses network byte order.
138+
139+
* 0 response has keys only (i.e. a `key_only:true` scan)
140+
* 1 response has keys + meta + documents (i.e. a `key_only:false` scan)
141+
125142
### Response Value Encoding `"key_only":true`
126143

127144
The mcbp header defines how much data follows, in this command only a value is
@@ -221,14 +238,25 @@ E.g. a document with key="key0" and value="value0"
221238
In the above layout, a second document would begin at offset 37, no padding or
222239
alignment.
223240

224-
### Success
241+
### Success status codes
242+
243+
A range-scan-continue response sequence indicates success using the following
244+
status codes.
245+
246+
**Status::Success 0x00**
247+
248+
Used for intermediate responses making up a larger response.
249+
250+
**Status::RangeScanMore 0xA6**
251+
252+
Scan has reached a limit and more data maybe available, the client must issue
253+
another continue.
254+
255+
**Status::RangeScanComplete 0xA7**
225256

226-
A range-scan-continue response sequence indicates success using status codes
257+
Scan has reached the end of the range, the scan itself will be removed from the
258+
server and any subsequent range-scan continue or cancel will be rejected.
227259

228-
* Status::Success 0x00: Used for intermediate responses making up a larger response.
229-
* Status::RangeScanMore 0xA6: Scan has reached a limit and has not reached the end
230-
key, more data maybe available and the client must issue another continue.
231-
* Status::RangeScanComplete 0xA7: Scan has reached the end of the range.
232260

233261
### Errors
234262

@@ -259,5 +287,4 @@ The collection was dropped.
259287

260288
**Status::RangeScanCancelled (0xA5)**
261289

262-
The scan was cancelled whilst returning data. This could be the only status if
263-
the cancel was noticed before a key/value was loaded.
290+
The scan was cancelled whilst returning data.

engines/ep/src/ep_vb.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1284,7 +1284,8 @@ std::pair<cb::engine_errc, cb::rangescan::Id> EPVBucket::createRangeScan(
12841284

12851285
// If no handler has been given, create one.
12861286
if (!handler) {
1287-
handler = std::make_unique<RangeScanDataHandler>(bucket->getEPEngine());
1287+
handler = std::make_unique<RangeScanDataHandler>(
1288+
bucket->getEPEngine(), keyOnly == cb::rangescan::KeyOnly::Yes);
12881289
}
12891290

12901291
// Create a task and give it the RangeScanCreateData, on failure the task

engines/ep/src/range_scans/range_scan_callbacks.cc

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@
2424
#include <memcached/range_scan_status.h>
2525
#include <statistics/cbstat_collector.h>
2626

27-
RangeScanDataHandler::RangeScanDataHandler(EventuallyPersistentEngine& engine)
27+
RangeScanDataHandler::RangeScanDataHandler(EventuallyPersistentEngine& engine,
28+
bool keyOnly)
2829
: engine(engine),
2930
sendTriggerThreshold(
30-
engine.getConfiguration().getRangeScanReadBufferSendSize()) {
31+
engine.getConfiguration().getRangeScanReadBufferSendSize()),
32+
keyOnly(keyOnly) {
3133
}
3234

3335
RangeScanDataHandler::Status RangeScanDataHandler::checkAndSend(
@@ -55,9 +57,11 @@ RangeScanDataHandler::Status RangeScanDataHandler::send(
5557
CookieIface& cookie, cb::engine_errc status) {
5658
bool sendSuccess = false;
5759
{
60+
cb::mcbp::response::RangeScanContinueResponseExtras extras(keyOnly);
5861
NonBucketAllocationGuard guard;
5962
sendSuccess = cookie.sendResponse(
6063
status,
64+
extras.getBuffer(),
6165
{reinterpret_cast<const char*>(responseBuffer.data()),
6266
responseBuffer.size()});
6367
}

engines/ep/src/range_scans/range_scan_callbacks.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ class RangeScanDataHandlerIFace {
100100
*/
101101
class RangeScanDataHandler : public RangeScanDataHandlerIFace {
102102
public:
103-
RangeScanDataHandler(EventuallyPersistentEngine& engine);
103+
RangeScanDataHandler(EventuallyPersistentEngine& engine, bool keyOnly);
104104

105105
Status handleKey(CookieIface& cookie, DocKey key) override;
106106

@@ -131,14 +131,16 @@ class RangeScanDataHandler : public RangeScanDataHandlerIFace {
131131
std::vector<uint8_t> responseBuffer;
132132

133133
/// the trigger for pushing data to send, set from engine configuration
134-
size_t sendTriggerThreshold{0};
134+
const size_t sendTriggerThreshold{0};
135135

136136
/**
137137
* As the scan continues, and reads data it accumulates how many bytes
138138
* are read in this member for use in checking the bucket throttle and
139139
* updating the metering counter.
140140
*/
141141
size_t pendingReadBytes{0};
142+
143+
const bool keyOnly{false};
142144
};
143145

144146
/**

include/mcbp/codec/range_scan_continue_codec.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,25 @@ class RangeScanContinueValuePayload {
8181
std::string_view payload;
8282
};
8383

84+
// The structure which is attached to all response packets from RangeScan
85+
// continue
86+
class RangeScanContinueResponseExtras {
87+
public:
88+
enum class Flags : uint32_t { KeyScan = 0, ValueScan = 1 };
89+
90+
RangeScanContinueResponseExtras(bool keyOnly);
91+
92+
std::string_view getBuffer() const {
93+
return {reinterpret_cast<const char*>(this), sizeof(*this)};
94+
}
95+
96+
Flags getFlags() const;
97+
98+
protected:
99+
uint32_t flags{0};
100+
};
101+
102+
static_assert(sizeof(RangeScanContinueResponseExtras) == 4,
103+
"Unexpected object size");
104+
84105
} // namespace cb::mcbp::response

include/memcached/cookie_iface.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,12 @@ class CookieIface : public cb::tracing::Traceable {
143143
/**
144144
* Send a status/value in a response message
145145
* @param status the status to use in the response message
146-
* @param value view onto a value to send
146+
* @param extras view onto extras to attach to the response
147+
* @param value view onto a value to attach to the response
147148
* @return true if successful, false if not (e.g. disconnect)
148149
*/
149150
virtual bool sendResponse(cb::engine_errc status,
151+
std::string_view extras,
150152
std::string_view value) = 0;
151153

152154
/**

programs/engine_testapp/mock_cookie.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,9 @@ bool MockCookie::checkThrottle(size_t, size_t) {
152152
return false;
153153
}
154154

155-
bool MockCookie::sendResponse(cb::engine_errc, std::string_view) {
155+
bool MockCookie::sendResponse(cb::engine_errc,
156+
std::string_view,
157+
std::string_view) {
156158
// do nothing
157159
return true;
158160
}

programs/engine_testapp/mock_cookie.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,9 @@ class MockCookie : public CookieIface {
169169
uint64_t manifestUid,
170170
bool metered) override;
171171
bool checkThrottle(size_t, size_t) override;
172-
bool sendResponse(cb::engine_errc status, std::string_view value) override;
172+
bool sendResponse(cb::engine_errc status,
173+
std::string_view extras,
174+
std::string_view value) override;
173175

174176
/// An alternative function to call for notifyIoComplete
175177
void setUserNotifyIoComplete(std::function<void(cb::engine_errc)> func) {

0 commit comments

Comments
 (0)