Skip to content

Commit 618e128

Browse files
committed
feat: Introduce debug sync point for testing
It can be useful that we can control execution of process by adding specific debug sync point which will be executed when debug sync name is set. For now this is only basic functionality, but in future we can add functionality for example - WAIT FOR until sync point is added. Debug sync point works with different fibers so we can synchronize execution. Added simple example how this can be done. Signed-off-by: mkaruza <[email protected]>
1 parent 11cfcd5 commit 618e128

File tree

5 files changed

+133
-0
lines changed

5 files changed

+133
-0
lines changed

src/server/debug_sync_point.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 205, DragonflyDB authors. All rights reserved.
2+
// See LICENSE for licensing terms.
3+
//
4+
5+
#include <absl/container/flat_hash_set.h>
6+
7+
#include "util/fibers/synchronization.h"
8+
9+
namespace dfly {
10+
11+
#ifdef NDEBUG
12+
#define DEBUG_SYNC(n, f)
13+
#else
14+
#define DEBUG_SYNC(n, f) \
15+
{ \
16+
if (debug_sync_point.Find(n)) \
17+
f(); \
18+
}
19+
#endif
20+
21+
class DebugSyncPoint {
22+
public:
23+
void Add(std::string_view name) {
24+
util::fb2::LockGuard lk(mu_);
25+
sync_points_.emplace(name);
26+
}
27+
28+
void Del(std::string_view name) {
29+
util::fb2::LockGuard lk(mu_);
30+
sync_points_.erase(name);
31+
}
32+
33+
bool Find(std::string_view name) {
34+
return sync_points_.find(name) != sync_points_.end();
35+
}
36+
37+
private:
38+
absl::flat_hash_set<std::string> sync_points_;
39+
util::fb2::Mutex mu_;
40+
};
41+
42+
inline DebugSyncPoint debug_sync_point;
43+
44+
} // namespace dfly

src/server/debugcmd.cc

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ extern "C" {
3333
#include "facade/cmd_arg_parser.h"
3434
#include "server/blocking_controller.h"
3535
#include "server/container_utils.h"
36+
#include "server/debug_sync_point.h"
3637
#include "server/engine_shard_set.h"
3738
#include "server/error.h"
3839
#include "server/main_service.h"
@@ -41,6 +42,7 @@ extern "C" {
4142
#include "server/server_state.h"
4243
#include "server/string_family.h"
4344
#include "server/transaction.h"
45+
4446
using namespace std;
4547

4648
ABSL_DECLARE_FLAG(string, dir);
@@ -657,6 +659,8 @@ void DebugCmd::Run(CmdArgList args, facade::SinkReplyBuilder* builder) {
657659
" per second.",
658660
"SEGMENTS",
659661
" Prints segment info for the current database.",
662+
"SYNC ADD [sync_name] | DEL [sync_name]",
663+
" Enable or disable debug sync point.",
660664
"HELP",
661665
" Prints this help.",
662666
};
@@ -741,6 +745,11 @@ void DebugCmd::Run(CmdArgList args, facade::SinkReplyBuilder* builder) {
741745
if (subcmd == "SEGMENTS") {
742746
return Segments(args.subspan(1), builder);
743747
}
748+
749+
if (subcmd == "SYNC") {
750+
return Sync(args, builder);
751+
}
752+
744753
string reply = UnknownSubCmd(subcmd, "DEBUG");
745754
return builder->SendError(reply, kSyntaxErrType);
746755
}
@@ -984,6 +993,48 @@ void DebugCmd::PopulateRangeFiber(uint64_t from, uint64_t num_of_keys,
984993
});
985994
}
986995

996+
// SYNC arguments format:
997+
// [ADD sync_point_name | DEL sync_point_name]
998+
std::optional<DebugCmd::SyncOptions> DebugCmd::ParseSyncArgs(CmdArgList args,
999+
facade::SinkReplyBuilder* builder) {
1000+
CmdArgParser parser(args.subspan(1));
1001+
SyncOptions options;
1002+
1003+
while (parser.HasNext()) {
1004+
SyncFlags flag = parser.MapNext("ADD", SYNC_ADD, "DEL", SYNC_DEL);
1005+
switch (flag) {
1006+
case SYNC_ADD:
1007+
case SYNC_DEL:
1008+
options.sync_point_name = parser.Next<string_view>();
1009+
options.sync_action = flag;
1010+
break;
1011+
default:
1012+
LOG(FATAL) << "Unexpected flag in PopulateArgs. Args: " << args;
1013+
break;
1014+
}
1015+
}
1016+
if (parser.HasError()) {
1017+
builder->SendError(parser.TakeError().MakeReply());
1018+
return nullopt;
1019+
}
1020+
return options;
1021+
}
1022+
1023+
void DebugCmd::Sync(CmdArgList args, facade::SinkReplyBuilder* builder) {
1024+
optional<SyncOptions> options = ParseSyncArgs(args, builder);
1025+
if (!options.has_value()) {
1026+
return;
1027+
}
1028+
1029+
if (options->sync_action == SYNC_ADD) {
1030+
debug_sync_point.Add(options->sync_point_name);
1031+
} else {
1032+
debug_sync_point.Del(options->sync_point_name);
1033+
}
1034+
1035+
builder->SendOk();
1036+
}
1037+
9871038
void DebugCmd::Exec(facade::SinkReplyBuilder* builder) {
9881039
EngineShardSet& ess = *shard_set;
9891040
fb2::Mutex mu;

src/server/debugcmd.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ class DebugCmd {
3030
std::optional<std::pair<uint32_t, uint32_t>> expire_ttl_range;
3131
};
3232

33+
enum SyncFlags { SYNC_ADD, SYNC_DEL };
34+
struct SyncOptions {
35+
std::string sync_point_name;
36+
SyncFlags sync_action;
37+
};
38+
3339
public:
3440
DebugCmd(ServerFamily* owner, cluster::ClusterFamily* cf, ConnectionContext* cntx);
3541

@@ -43,6 +49,10 @@ class DebugCmd {
4349
facade::SinkReplyBuilder* builder);
4450
void PopulateRangeFiber(uint64_t from, uint64_t count, const PopulateOptions& opts);
4551

52+
static std::optional<SyncOptions> ParseSyncArgs(CmdArgList args,
53+
facade::SinkReplyBuilder* builder);
54+
void Sync(CmdArgList args, facade::SinkReplyBuilder* builder);
55+
4656
void Reload(CmdArgList args, facade::SinkReplyBuilder* builder);
4757
void Replica(CmdArgList args, facade::SinkReplyBuilder* builder);
4858
void Migration(CmdArgList args, facade::SinkReplyBuilder* builder);

src/server/string_family.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "server/command_registry.h"
2727
#include "server/common.h"
2828
#include "server/conn_context.h"
29+
#include "server/debug_sync_point.h"
2930
#include "server/engine_shard_set.h"
3031
#include "server/error.h"
3132
#include "server/family_utils.h"
@@ -1178,6 +1179,9 @@ void StringFamily::SetNx(CmdArgList args, const CommandContext& cmnd_cntx) {
11781179
void StringFamily::Get(CmdArgList args, const CommandContext& cmnd_cntx) {
11791180
auto cb = [key = ArgS(args, 0)](Transaction* tx, EngineShard* es) -> OpResult<StringResult> {
11801181
auto it_res = tx->GetDbSlice(es->shard_id()).FindReadOnly(tx->GetDbContext(), key, OBJ_STRING);
1182+
1183+
DEBUG_SYNC("return_get_key_not_found", [&]() { it_res = OpStatus::KEY_NOTFOUND; })
1184+
11811185
if (!it_res.ok())
11821186
return it_res.status();
11831187

tests/dragonfly/generic_test.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,3 +342,27 @@ async def test_key_bump_ups(df_factory):
342342
new_slot_id = int(dict(map(lambda s: s.split(":"), debug_key_info.split()))["slot"])
343343
assert new_slot_id + 1 == slot_id
344344
break
345+
346+
347+
@pytest.mark.asyncio
348+
async def test_debug_sync(df_factory):
349+
df_server = df_factory.create()
350+
df_server.start()
351+
client = df_server.client()
352+
353+
value = "123"
354+
355+
# Should return key
356+
await client.execute_command(f"SET key {value}")
357+
result = await client.get("key")
358+
assert result == value
359+
360+
# Enable debug sync point to return KEY_NOTFOUND
361+
await client.execute_command("DEBUG SYNC ADD return_get_key_not_found")
362+
result = await client.get("key")
363+
assert result == None
364+
365+
# Disable - should return key
366+
await client.execute_command("DEBUG SYNC DEL return_get_key_not_found")
367+
result = await client.get("key")
368+
assert result == value

0 commit comments

Comments
 (0)