Skip to content
This repository was archived by the owner on Jun 26, 2023. It is now read-only.

Commit 22301e2

Browse files
committed
EOS-26465: Non-batch put kv
Signed-off-by: Dattaprasad Govekar <dattaprasad.govekar@seagate.com>
1 parent 90a07d2 commit 22301e2

15 files changed

Lines changed: 471 additions & 61 deletions

server/s3_motr_kvs_writer.cc

Lines changed: 152 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ S3MotrKVSWriter::S3MotrKVSWriter(std::shared_ptr<RequestObject> req,
4646
} else {
4747
s3_motr_api = std::make_shared<ConcreteMotrAPI>();
4848
}
49+
on_success_for_parallelkv =
50+
std::bind(&S3MotrKVSWriter::parallel_put_kv_successful, this,
51+
std::placeholders::_1);
52+
on_failure_for_parallelkv = std::bind(
53+
&S3MotrKVSWriter::parallel_put_kv_failed, this, std::placeholders::_1);
4954
}
5055

5156
S3MotrKVSWriter::S3MotrKVSWriter(std::string req_id,
@@ -61,6 +66,11 @@ S3MotrKVSWriter::S3MotrKVSWriter(std::string req_id,
6166
} else {
6267
s3_motr_api = std::make_shared<ConcreteMotrAPI>();
6368
}
69+
on_success_for_parallelkv =
70+
std::bind(&S3MotrKVSWriter::parallel_put_kv_successful, this,
71+
std::placeholders::_1);
72+
on_failure_for_parallelkv = std::bind(
73+
&S3MotrKVSWriter::parallel_put_kv_failed, this, std::placeholders::_1);
6474
}
6575

6676
S3MotrKVSWriter::~S3MotrKVSWriter() {
@@ -479,11 +489,88 @@ void S3MotrKVSWriter::delete_indices_failed() {
479489
s3_log(S3_LOG_DEBUG, "", "%s Exit", __func__);
480490
}
481491

492+
void S3MotrKVSWriter::parallel_put_kv_successful(unsigned int processed_count) {
493+
s3_log(S3_LOG_DEBUG, request_id, "%s Entry\n", __func__);
494+
next_key_offset += processed_count;
495+
kvop_keys_in_flight -= 1;
496+
unsigned int total_keys = kv_list.size();
497+
s3_log(S3_LOG_DEBUG, request_id, "Total writer contexts = %zu",
498+
parallel_writer_contexts.size());
499+
#if 0
500+
// Early free of writer context
501+
if ((next_key_offset - 1) < parallel_writer_contexts.size()) {
502+
s3_log(S3_LOG_DEBUG, request_id, "Freeing writer context at index = %d",
503+
(next_key_offset - 1));
504+
parallel_writer_contexts[next_key_offset - 1].reset(NULL);
505+
s3_log(S3_LOG_DEBUG, request_id, "Outstanding writer contexts = %zu",
506+
parallel_writer_contexts.size() - next_key_offset);
507+
}
508+
#endif
509+
510+
if ((next_key_offset + kvop_keys_in_flight) < total_keys &&
511+
!atleast_one_failed) {
512+
// Still more to launch PUT KV
513+
if (kvop_keys_in_flight < max_parallel_kv) {
514+
// Current number of parallel KVs is less than max allowed.
515+
// Launch next PUT KV operation
516+
put_partial_keyval(idx_los[0], kv_list, on_success_for_parallelkv,
517+
on_failure_for_parallelkv,
518+
next_key_offset + kvop_keys_in_flight, 1, false);
519+
++kvop_keys_in_flight;
520+
// Save writer context
521+
parallel_writer_contexts.push_back(std::move(this->get_writer_context()));
522+
}
523+
} else {
524+
// All PUT KVs done(all success or atleast one fail). Perform next step
525+
if (atleast_one_failed && kvop_keys_in_flight == 0) {
526+
// One of PUT KV failed previously, and there is no outstanding PUT KV
527+
// that we need to wait from the batch of parallel PUT KV.
528+
// Perform next step: Failure
529+
// TODO: Need to rollback all successfull PUT KV
530+
handler_on_failed();
531+
return;
532+
}
533+
if (!atleast_one_failed && kvop_keys_in_flight == 0) {
534+
// All keys are done, and all PUT KV in parallel
535+
// operation are done. Perform next step: Success
536+
handler_on_success();
537+
return;
538+
}
539+
// Need to wait for reminaing PUT KV from the batch of parallel
540+
// PUT KV
541+
}
542+
s3_log(S3_LOG_DEBUG, "", "%s Exit", __func__);
543+
}
544+
545+
void S3MotrKVSWriter::parallel_put_kv_failed(unsigned int processed_count) {
546+
s3_log(S3_LOG_DEBUG, request_id, "%s Entry\n", __func__);
547+
next_key_offset += processed_count;
548+
atleast_one_failed = true;
549+
kvop_keys_in_flight -= 1;
550+
s3_log(S3_LOG_DEBUG, request_id, "Total writer contexts = %zu",
551+
parallel_writer_contexts.size());
552+
#if 0
553+
// Early free of writer context
554+
if ((next_key_offset - 1) < parallel_writer_contexts.size()) {
555+
s3_log(S3_LOG_DEBUG, request_id, "Freeing writer context at index = %d",
556+
(next_key_offset - 1));
557+
parallel_writer_contexts[next_key_offset - 1].reset(NULL);
558+
s3_log(S3_LOG_DEBUG, request_id, "Outstanding writer contexts = %zu",
559+
parallel_writer_contexts.size() - next_key_offset);
560+
}
561+
#endif
562+
if (kvop_keys_in_flight == 0) {
563+
// All the keys in parallel batch are done, perform next step
564+
handler_on_failed();
565+
}
566+
s3_log(S3_LOG_DEBUG, "", "%s Exit", __func__);
567+
}
568+
482569
void S3MotrKVSWriter::put_keyval(
483570
const struct s3_motr_idx_layout &idx_lo,
484571
const std::map<std::string, std::string> &kv_list,
485572
std::function<void(void)> on_success, std::function<void(void)> on_failed,
486-
S3MotrKVSWriter::CallbackType callback) {
573+
CallbackType callback, bool parallel_mode) {
487574

488575
s3_log(S3_LOG_INFO, stripped_request_id,
489576
"%s Entry with oid = %" SCNx64 " : %" SCNx64
@@ -499,24 +586,47 @@ void S3MotrKVSWriter::put_keyval(
499586
s3_log(S3_LOG_ERROR, request_id, "Empty key in PUT KV\n");
500587
}
501588
}
502-
idx_los.clear();
503-
idx_los.push_back(idx_lo);
504589

505590
this->handler_on_success = std::move(on_success);
506591
this->handler_on_failed = std::move(on_failed);
507592

508-
if (idx_ctx) {
509-
// clean up any old allocations
510-
clean_up_contexts();
511-
}
512-
idx_ctx = create_idx_context(1);
593+
if (!this->parallel_run || parallel_mode) {
594+
// Initialize once in parallel mode
595+
idx_los.clear();
596+
idx_los.push_back(idx_lo);
513597

598+
if (idx_ctx) {
599+
// clean up any old allocations
600+
clean_up_contexts();
601+
}
602+
idx_ctx = create_idx_context(1);
603+
}
604+
if (parallel_mode && kv_list.size() > 1) {
605+
// Parallel mode PUT kv, provided list contains at least 2 keys
606+
this->kv_list = kv_list;
607+
next_key_offset = 0;
608+
parallel_writer_contexts.clear();
609+
const int parallel_kvs =
610+
(kv_list.size() <= max_parallel_kv) ? kv_list.size() : max_parallel_kv;
611+
this->parallel_run = parallel_mode;
612+
s3_log(S3_LOG_DEBUG, request_id, "Parallel PUT kv, with %d kv in parallel",
613+
parallel_kvs);
614+
for (int i = 0; i < parallel_kvs; ++i) {
615+
// Call below in non-parallel mode
616+
put_partial_keyval(idx_lo, kv_list, on_success_for_parallelkv,
617+
on_failure_for_parallelkv, i, 1, false);
618+
++kvop_keys_in_flight;
619+
// Save writer context
620+
parallel_writer_contexts.push_back(std::move(this->get_writer_context()));
621+
}
622+
// End of Parallel mode
623+
return;
624+
}
514625
writer_context.reset(new S3AsyncMotrKVSWriterContext(
515626
request, std::bind(&S3MotrKVSWriter::put_keyval_successful, this),
516627
std::bind(&S3MotrKVSWriter::put_keyval_failed, this), 1, s3_motr_api));
517628

518629
writer_context->init_kvs_write_op_ctx(kv_list.size());
519-
520630
// Ret code can be ignored as its already handled in async case.
521631
put_keyval_impl(kv_list, true, false, 0, 0, callback);
522632
}
@@ -526,7 +636,7 @@ void S3MotrKVSWriter::put_partial_keyval(
526636
const std::map<std::string, std::string> &kv_list,
527637
std::function<void(unsigned int)> on_success,
528638
std::function<void(unsigned int)> on_failed, unsigned int offset,
529-
unsigned int how_many) {
639+
unsigned int how_many, bool parallel_mode) {
530640
std::map<std::string, std::string>::const_iterator it;
531641
unsigned int record_count = 0;
532642
assert(how_many != 0);
@@ -557,17 +667,41 @@ void S3MotrKVSWriter::put_partial_keyval(
557667
break;
558668
}
559669
}
560-
idx_los.clear();
561-
idx_los.push_back(idx_lo);
562-
563670
this->on_success_for_extends = std::move(on_success);
564671
this->on_failure_for_extends = std::move(on_failed);
565672

566-
if (idx_ctx) {
567-
// clean up any old allocations
568-
clean_up_contexts();
673+
if (!this->parallel_run || parallel_mode) {
674+
// Initialize once in parallel mode
675+
idx_los.clear();
676+
idx_los.push_back(idx_lo);
677+
678+
if (idx_ctx) {
679+
// clean up any old allocations
680+
clean_up_contexts();
681+
}
682+
idx_ctx = create_idx_context(1);
683+
}
684+
if (parallel_mode && how_many > 1) {
685+
// Parallel mode PUT partial kv, provided list contains at least 2 keys
686+
this->kv_list = kv_list;
687+
next_key_offset = 0;
688+
parallel_writer_contexts.clear();
689+
const int parallel_kvs =
690+
(how_many <= max_parallel_kv) ? how_many : max_parallel_kv;
691+
this->parallel_run = parallel_mode;
692+
s3_log(S3_LOG_DEBUG, request_id, "Parallel PUT kv, with %d kv in parallel",
693+
parallel_kvs);
694+
for (int i = 0; i < parallel_kvs; ++i) {
695+
// Call below in non-parallel mode
696+
put_partial_keyval(idx_lo, kv_list, on_success_for_parallelkv,
697+
on_failure_for_parallelkv, i, 1, false);
698+
++kvop_keys_in_flight;
699+
// Save writer context
700+
parallel_writer_contexts.push_back(std::move(this->get_writer_context()));
701+
}
702+
// End of Parallel mode
703+
return;
569704
}
570-
idx_ctx = create_idx_context(1);
571705
writer_context.reset(new S3AsyncMotrKVSWriterContext(
572706
request, std::bind(&S3MotrKVSWriter::put_partial_keyval_successful, this),
573707
std::bind(&S3MotrKVSWriter::put_partial_keyval_failed, this), 1,
@@ -720,7 +854,7 @@ void S3MotrKVSWriter::put_keyval(const struct s3_motr_idx_layout &idx_lo,
720854
const std::string &key, const std::string &val,
721855
std::function<void(void)> on_success,
722856
std::function<void(void)> on_failed,
723-
S3MotrKVSWriter::CallbackType callback) {
857+
CallbackType callback) {
724858
s3_log(S3_LOG_INFO, stripped_request_id,
725859
"%s Entry with oid = %" SCNx64 " : %" SCNx64
726860
" key = %s and value = %s\n",

server/s3_motr_kvs_writer.h

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@
2626
#include <gtest/gtest_prod.h>
2727
#include <functional>
2828
#include <memory>
29+
#include <deque>
2930

3031
#include "s3_asyncop_context_base.h"
3132
#include "s3_motr_context.h"
3233
#include "s3_motr_wrapper.h"
3334
#include "s3_log.h"
3435
#include "s3_request_object.h"
36+
#define MAX_PARALLEL_KVS 30
3537

3638
class S3SyncMotrKVSWriterContext {
3739
// Basic Operation context.
@@ -139,7 +141,18 @@ class S3MotrKVSWriter {
139141
std::unique_ptr<S3AsyncMotrKVSWriterContext> sync_context;
140142
std::string kvs_key;
141143
std::string kvs_value;
144+
145+
// Following are used for parallel KV operation
142146
unsigned int how_many_being_processed;
147+
// Number of Keys/values in KV operation in flight (in parallel)
148+
unsigned int kvop_keys_in_flight = 0;
149+
bool parallel_run = false;
150+
bool atleast_one_failed = false;
151+
std::map<std::string, std::string> kv_list;
152+
unsigned int next_key_offset = 0;
153+
std::deque<std::unique_ptr<S3AsyncMotrKVSWriterContext>>
154+
parallel_writer_contexts;
155+
unsigned int max_parallel_kv = MAX_PARALLEL_KVS;
143156

144157
std::string request_id;
145158
std::string stripped_request_id;
@@ -155,6 +168,9 @@ class S3MotrKVSWriter {
155168
struct s3_motr_idx_context* idx_ctx;
156169

157170
void clean_up_contexts();
171+
std::unique_ptr<S3AsyncMotrKVSWriterContext>& get_writer_context() {
172+
return writer_context;
173+
}
158174

159175
void create_index_successful();
160176
void create_index_failed();
@@ -179,6 +195,12 @@ class S3MotrKVSWriter {
179195
S3MotrKVSWriter::CallbackType
180196
callback = S3MotrKVSWriter::CallbackType::STABLE);
181197

198+
protected:
199+
void parallel_put_kv_successful(unsigned int);
200+
void parallel_put_kv_failed(unsigned int);
201+
std::function<void(unsigned int)> on_success_for_parallelkv;
202+
std::function<void(unsigned int)> on_failure_for_parallelkv;
203+
182204
public:
183205
S3MotrKVSWriter(std::shared_ptr<RequestObject> req,
184206
std::shared_ptr<MotrAPI> motr_api = nullptr);
@@ -223,26 +245,30 @@ class S3MotrKVSWriter {
223245
std::function<void(void)> on_success,
224246
std::function<void(void)> on_failed);
225247

226-
virtual void put_keyval(const struct s3_motr_idx_layout& idx_lo,
227-
const std::map<std::string, std::string>& kv_list,
228-
std::function<void(void)> on_success,
229-
std::function<void(void)> on_failed,
230-
S3MotrKVSWriter::CallbackType
231-
callback = S3MotrKVSWriter::CallbackType::STABLE);
248+
// When parallel_mode = true (default), the function will perform several
249+
// single
250+
// PUT kv for keys in the list in parallel. When all PUT kv are successfull,
251+
// on_success would be called, else on_failed.
252+
virtual void put_keyval(
253+
const struct s3_motr_idx_layout& idx_lo,
254+
const std::map<std::string, std::string>& kv_list,
255+
std::function<void(void)> on_success, std::function<void(void)> on_failed,
256+
CallbackType callback = S3MotrKVSWriter::CallbackType::STABLE,
257+
bool parallel_mode = true);
232258

233259
virtual void put_partial_keyval(
234260
const struct s3_motr_idx_layout& idx_lo,
235261
const std::map<std::string, std::string>& kv_list,
236262
std::function<void(unsigned int)> on_success,
237263
std::function<void(unsigned int)> on_failed, unsigned int offset = 0,
238-
unsigned int how_many = 30);
264+
unsigned int how_many = 30, bool parallel_mode = true);
239265

240266
// Async save operation.
241267
virtual void put_keyval(
242268
const struct s3_motr_idx_layout& idx_lo, const std::string& key,
243269
const std::string& val, std::function<void(void)> on_success,
244270
std::function<void(void)> on_failed,
245-
enum CallbackType callback = S3MotrKVSWriter::CallbackType::STABLE);
271+
CallbackType callback = S3MotrKVSWriter::CallbackType::STABLE);
246272
// Sync save operation.
247273
virtual int put_keyval_sync(
248274
const struct s3_motr_idx_layout& idx_lo,
@@ -307,6 +333,8 @@ class S3MotrKVSWriter {
307333
FRIEND_TEST(S3PartMetadataTest, CreatePartIndexSuccessfulSaveMetadata);
308334
FRIEND_TEST(S3NewAccountRegisterNotifyActionTest,
309335
CreateBucketListIndexSuccessful);
336+
FRIEND_TEST(S3MotrKVSWritterTest, ParallelPutKeyValSuccessful);
337+
FRIEND_TEST(S3MotrKVSWritterTest, ParallelPutKeyValFail);
310338
};
311339

312340
#endif

ut/mock_s3_motr_kvs_writer.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ class MockS3MotrKVSWriter : public S3MotrKVSWriter {
5959
const std::string&, std::function<void(void)> on_success,
6060
std::function<void(void)> on_failed,
6161
CallbackType callback));
62-
MOCK_METHOD5(put_keyval,
62+
MOCK_METHOD6(put_keyval,
6363
void(const struct s3_motr_idx_layout&,
6464
const std::map<std::string, std::string>& kv_list,
6565
std::function<void(void)> on_success,
66-
std::function<void(void)> on_failed,
67-
CallbackType callback));
66+
std::function<void(void)> on_failed, CallbackType callback,
67+
bool parallel));
6868
};
6969
#endif

ut/s3_abort_multipart_test.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,9 @@ TEST_F(S3AbortMultipartActionTest, Send200SuccessToS3Client) {
298298
// expectations for mark_oid_for_deletion()
299299
EXPECT_CALL(*prob_rec, set_force_delete(true)).Times(1);
300300
EXPECT_CALL(*prob_rec, to_json()).Times(1);
301+
bool parallel = true;
301302
EXPECT_CALL(*(motr_kvs_writer_factory->mock_motr_kvs_writer),
302-
put_keyval(_, _, _, _, _)).Times(1);
303+
put_keyval(_, _, _, _, _, parallel)).Times(1);
303304

304305
EXPECT_CALL(*ptr_mock_request, send_response(200, _)).Times(1);
305306
action_under_test->send_response_to_s3_client();

0 commit comments

Comments
 (0)