Skip to content
41 changes: 41 additions & 0 deletions src/server/search/search_family_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2768,6 +2768,47 @@ TEST_F(SearchFamilyTest, JsonSetIndexesBug) {
EXPECT_THAT(resp, IsUnordArrayWithSize(IsMap("text", "some text")));
}

TEST_F(SearchFamilyTest, SearchReindexWriteSearchRace) {
const std::string kIndexName = "myRaceIdx";
const int kWriterOps = 1000;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it reproduces for me with all constants be 100

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please check if also for you. I prefer not having long running unit tests

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated to 200 because 100 sometimes passes

const int kSearcherOps = 1000;
const int kReindexerOps = 500;

auto writer_fiber = pp_->at(0)->LaunchFiber([&] {
for (int i = 1; i <= kWriterOps; ++i) {
std::string doc_key = absl::StrCat("doc:", i);
std::string content = absl::StrCat("text data item ", i, " for race condition test");
std::string tags_val = absl::StrCat("tagA,tagB,", (i % 10));
std::string numeric_field_val = std::to_string(i);
Run({"hset", doc_key, "content", content, "tags", tags_val, "numeric_field",
numeric_field_val});
}
});

auto searcher_fiber = pp_->at(1)->LaunchFiber([&] {
for (int i = 1; i <= kSearcherOps; ++i) {
int random_val_content = 1 + (i % kWriterOps);
std::string query_content = absl::StrCat("@content:item", random_val_content);
Run({"ft.search", kIndexName, query_content});
}
});

auto reindexer_fiber = pp_->at(2)->LaunchFiber([&] {
for (int i = 1; i <= kReindexerOps; ++i) {
Run({"ft.create", kIndexName, "ON", "HASH", "PREFIX", "1", "doc:", "SCHEMA", "content",
"TEXT", "SORTABLE", "tags", "TAG", "SORTABLE", "numeric_field", "NUMERIC", "SORTABLE"});
Run({"ft.dropindex", kIndexName});
}
});

// Join fibers
writer_fiber.Join();
searcher_fiber.Join();
reindexer_fiber.Join();

ASSERT_FALSE(service_->IsShardSetLocked());
}

TEST_F(SearchFamilyTest, IgnoredOptionsInFtCreate) {
Run({"HSET", "doc:1", "title", "Test Document"});

Expand Down
13 changes: 10 additions & 3 deletions src/server/transaction.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1245,9 +1245,16 @@ bool Transaction::CancelShardCb(EngineShard* shard) {
shard->shard_lock()->Release(LockMode());
} else {
auto lock_args = GetLockArgs(shard->shard_id());
DCHECK(sd.local_mask & KEYLOCK_ACQUIRED);
DCHECK(!lock_args.fps.empty());
GetDbSlice(shard->shard_id()).Release(LockMode(), lock_args);

if (lock_args.fps.empty()) {
// For NO_KEY_TRANSACTIONAL commands, we don't need to release locks
// because we don't acquire locks for them.
DCHECK(cid_->opt_mask() & CO::NO_KEY_TRANSACTIONAL);
} else {
DCHECK(sd.local_mask & KEYLOCK_ACQUIRED);
GetDbSlice(shard->shard_id()).Release(LockMode(), lock_args);
}

sd.local_mask &= ~KEYLOCK_ACQUIRED;
}

Expand Down
Loading