Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions python/ray/experimental/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,14 +243,8 @@ def _object_table(self, object_id):
object_info = {
"DataSize": entry.ObjectSize(),
"Manager": entry.Manager(),
"IsEviction": [entry.IsEviction()],
}

for i in range(1, gcs_entry.EntriesLength()):
entry = ray.gcs_utils.ObjectTableData.GetRootAsObjectTableData(
gcs_entry.Entries(i), 0)
object_info["IsEviction"].append(entry.IsEviction())

return object_info

def object_table(self, object_id=None):
Expand Down
4 changes: 0 additions & 4 deletions python/ray/tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2467,10 +2467,6 @@ def wait_for_object_table():
object_table = ray.global_state.object_table()
assert len(object_table) == 2

assert object_table[x_id]["IsEviction"][0] is False

assert object_table[result_id]["IsEviction"][0] is False

assert object_table[x_id] == ray.global_state.object_table(x_id)
object_table_entry = ray.global_state.object_table(result_id)
assert object_table[result_id] == object_table_entry
Expand Down
2 changes: 1 addition & 1 deletion src/ray/gcs/client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ AsyncGcsClient::AsyncGcsClient(const std::string &address, int port,
driver_table_.reset(new DriverTable({primary_context_}, this));
heartbeat_batch_table_.reset(new HeartbeatBatchTable({primary_context_}, this));
// Tables below would be sharded.
object_table_.reset(new ObjectTable(shard_contexts_, this, command_type));
object_table_.reset(new ObjectTable(shard_contexts_, this));
raylet_task_table_.reset(new raylet::TaskTable(shard_contexts_, this, command_type));
task_reconstruction_log_.reset(new TaskReconstructionLog(shard_contexts_, this));
task_lease_table_.reset(new TaskLeaseTable(shard_contexts_, this));
Expand Down
533 changes: 454 additions & 79 deletions src/ray/gcs/client_test.cc

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions src/ray/gcs/format/gcs.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,13 @@ table ResourcePair {
value: double;
}

enum GcsTableNotificationMode:int {
APPEND_OR_ADD = 0,
REMOVE,
}

table GcsTableEntry {
mode: GcsTableNotificationMode;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

maybe rename mode to notification_mode, whose meaning is more clear.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks! Updated.

id: string;
entries: [string];
}
Expand All @@ -124,8 +130,6 @@ table ObjectTableData {
object_size: long;
// The node manager ID that this object appeared on or was evicted by.
manager: string;
// Whether this entry is an addition or a deletion.
is_eviction: bool;
}

table TaskReconstructionData {
Expand Down
160 changes: 145 additions & 15 deletions src/ray/gcs/redis_module/ray_redis_module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -181,22 +181,24 @@ flatbuffers::Offset<flatbuffers::String> RedisStringToFlatbuf(
return fbb.CreateString(redis_string_str, redis_string_size);
}

/// Publish a notification for a new entry at a key. This publishes a
/// Publish a notification for an entry update at a key. This publishes a
/// notification to all subscribers of the table, as well as every client that
/// has requested notifications for this key.
///
/// \param pubsub_channel_str The pubsub channel name that notifications for
/// this key should be published to. When publishing to a specific
/// client, the channel name should be <pubsub_channel>:<client_id>.
/// \param id The ID of the key that the notification is about.
/// \param data The data to publish.
/// \param mode the update mode, such as append or remove.
/// \param data The appended/removed data.
/// \return OK if there is no error during a publish.
int PublishTableAdd(RedisModuleCtx *ctx, RedisModuleString *pubsub_channel_str,
RedisModuleString *id, RedisModuleString *data) {
int PublishTableUpdate(RedisModuleCtx *ctx, RedisModuleString *pubsub_channel_str,
RedisModuleString *id, GcsTableNotificationMode mode,
RedisModuleString *data) {
// Serialize the notification to send.
flatbuffers::FlatBufferBuilder fbb;
auto data_flatbuf = RedisStringToFlatbuf(fbb, data);
auto message = CreateGcsTableEntry(fbb, RedisStringToFlatbuf(fbb, id),
auto message = CreateGcsTableEntry(fbb, mode, RedisStringToFlatbuf(fbb, id),
fbb.CreateVector(&data_flatbuf, 1));
fbb.Finish(message);

Expand Down Expand Up @@ -265,7 +267,8 @@ int TableAdd_DoPublish(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)

if (pubsub_channel != TablePubsub::NO_PUBLISH) {
// All other pubsub channels write the data back directly onto the channel.
return PublishTableAdd(ctx, pubsub_channel_str, id, data);
return PublishTableUpdate(ctx, pubsub_channel_str, id,
GcsTableNotificationMode::APPEND_OR_ADD, data);
} else {
return RedisModule_ReplyWithSimpleString(ctx, "OK");
}
Expand Down Expand Up @@ -364,7 +367,8 @@ int TableAppend_DoPublish(RedisModuleCtx *ctx, RedisModuleString **argv, int /*a
if (pubsub_channel != TablePubsub::NO_PUBLISH) {
// All other pubsub channels write the data back directly onto the
// channel.
return PublishTableAdd(ctx, pubsub_channel_str, id, data);
return PublishTableUpdate(ctx, pubsub_channel_str, id,
GcsTableNotificationMode::APPEND_OR_ADD, data);
} else {
return RedisModule_ReplyWithSimpleString(ctx, "OK");
}
Expand Down Expand Up @@ -407,6 +411,110 @@ int ChainTableAppend_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
}
#endif

int Set_DoPublish(RedisModuleCtx *ctx, RedisModuleString **argv, bool is_add) {
RedisModuleString *pubsub_channel_str = argv[2];
RedisModuleString *id = argv[3];
RedisModuleString *data = argv[4];
// Publish a message on the requested pubsub channel if necessary.
TablePubsub pubsub_channel;
REPLY_AND_RETURN_IF_NOT_OK(ParseTablePubsub(&pubsub_channel, pubsub_channel_str));
if (pubsub_channel != TablePubsub::NO_PUBLISH) {
// All other pubsub channels write the data back directly onto the
// channel.
return PublishTableUpdate(ctx, pubsub_channel_str, id,
is_add ? GcsTableNotificationMode::APPEND_OR_ADD
: GcsTableNotificationMode::REMOVE,
data);
} else {
return RedisModule_ReplyWithSimpleString(ctx, "OK");
}
}

int Set_DoWrite(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, bool is_add, bool &changed) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The convention we've been using is to use pointers instead of references for function outputs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just learnt that google style also requires using references for output args, https://google.github.io/styleguide/cppguide.html#Reference_Arguments

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks! Updated to bool *

if (argc != 5) {
return RedisModule_WrongArity(ctx);
}

RedisModuleString *prefix_str = argv[1];
RedisModuleString *id = argv[3];
RedisModuleString *data = argv[4];

RedisModuleString *key_string = PrefixedKeyString(ctx, prefix_str, id);
RedisModuleCallReply *reply =
RedisModule_Call(ctx, is_add ? "SADD" : "SREM", "ss", key_string, data);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

RedisModule_Call seems slower than the direct c++ interface. Is that because there is no such interface ready for SREM? If that is the case, please add a TODO comment to replace this command to C++ interface if hredis is updated..

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, set type API is not available yet. See https://redis.io/topics/modules-intro

if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_ERROR) {
changed = RedisModule_CallReplyInteger(reply) > 0;
if (!is_add) {
// try to delete the empty set.
RedisModuleKey *key;
REPLY_AND_RETURN_IF_NOT_OK(
OpenPrefixedKey(&key, ctx, prefix_str, id, REDISMODULE_WRITE));
auto size = RedisModule_ValueLength(key);
if (size == 0) {
REPLY_AND_RETURN_IF_FALSE(RedisModule_DeleteKey(key) == REDISMODULE_OK,
"Failed to delete empty set.");
}
}
return REDISMODULE_OK;
} else {
// the SADD/SREM command failed
RedisModule_ReplyWithCallReply(ctx, reply);
return REDISMODULE_ERR;
}
}

/// Add an entry to the set stored at a key. Publishes a notification about
/// the update to all subscribers, if a pubsub channel is provided.
///
/// This is called from a client with the command:
//
/// RAY.SET_ADD <table_prefix> <pubsub_channel> <id> <data>
///
/// \param table_prefix The prefix string for keys in this set.
/// \param pubsub_channel The pubsub channel name that notifications for
/// this key should be published to. When publishing to a specific
/// client, the channel name should be <pubsub_channel>:<client_id>.
/// \param id The ID of the key to add to.
/// \param data The data to add to the key.
/// \return OK if the add succeeds, or an error message string if the add
/// fails.
int SetAdd_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
bool changed;
if (Set_DoWrite(ctx, argv, argc, /*is_add=*/true, changed) != REDISMODULE_OK) {
return REDISMODULE_ERR;
}
if (changed) {
return Set_DoPublish(ctx, argv, /*is_add=*/true);
}
return REDISMODULE_OK;
}

/// Remove an entry from the set stored at a key. Publishes a notification about
/// the update to all subscribers, if a pubsub channel is provided.
///
/// This is called from a client with the command:
//
/// RAY.SET_REMOVE <table_prefix> <pubsub_channel> <id> <data>
///
/// \param table_prefix The prefix string for keys in this table.
/// \param pubsub_channel The pubsub channel name that notifications for
/// this key should be published to. When publishing to a specific
/// client, the channel name should be <pubsub_channel>:<client_id>.
/// \param id The ID of the key to remove from.
/// \param data The data to remove from the key.
/// \return OK if the remove succeeds, or an error message string if the remove
/// fails.
int SetRemove_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
bool changed;
if (Set_DoWrite(ctx, argv, argc, /*is_add=*/false, changed) != REDISMODULE_OK) {
return REDISMODULE_ERR;
}
if (changed) {
return Set_DoPublish(ctx, argv, /*is_add=*/false);
}
return REDISMODULE_OK;
}

/// A helper function to create and finish a GcsTableEntry, based on the
/// current value or values at the given key.
///
Expand All @@ -428,22 +536,31 @@ Status TableEntryToFlatbuf(RedisModuleCtx *ctx, RedisModuleKey *table_key,
size_t data_len = 0;
char *data_buf = RedisModule_StringDMA(table_key, &data_len, REDISMODULE_READ);
auto data = fbb.CreateString(data_buf, data_len);
auto message = CreateGcsTableEntry(fbb, RedisStringToFlatbuf(fbb, entry_id),
auto message = CreateGcsTableEntry(fbb, GcsTableNotificationMode::APPEND_OR_ADD,
RedisStringToFlatbuf(fbb, entry_id),
fbb.CreateVector(&data, 1));
fbb.Finish(message);
} break;
case REDISMODULE_KEYTYPE_LIST: {
case REDISMODULE_KEYTYPE_LIST:
case REDISMODULE_KEYTYPE_SET: {
RedisModule_CloseKey(table_key);
// Close the key before executing the command. NOTE(swang): According to
// https://github.com/RedisLabs/RedisModulesSDK/blob/master/API.md, "While
// a key is open, it should only be accessed via the low level key API."
RedisModuleString *table_key_str = PrefixedKeyString(ctx, prefix_str, entry_id);
// TODO(swang): This could potentially be replaced with the native redis
// server list iterator, once it is implemented for redis modules.
RedisModuleCallReply *reply =
RedisModule_Call(ctx, "LRANGE", "sll", table_key_str, 0, -1);
RedisModuleCallReply *reply = nullptr;
switch (key_type) {
case REDISMODULE_KEYTYPE_LIST:
reply = RedisModule_Call(ctx, "LRANGE", "sll", table_key_str, 0, -1);
break;
case REDISMODULE_KEYTYPE_SET:
reply = RedisModule_Call(ctx, "SMEMBERS", "s", table_key_str);
break;
}
// Build the flatbuffer from the set of log entries.
if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_ARRAY) {
if (reply == nullptr || RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_ARRAY) {
return Status::RedisError("Empty list or wrong type");
}
std::vector<flatbuffers::Offset<flatbuffers::String>> data;
Expand All @@ -453,13 +570,14 @@ Status TableEntryToFlatbuf(RedisModuleCtx *ctx, RedisModuleKey *table_key,
const char *element_str = RedisModule_CallReplyStringPtr(element, &len);
data.push_back(fbb.CreateString(element_str, len));
}
auto message = CreateGcsTableEntry(fbb, RedisStringToFlatbuf(fbb, entry_id),
fbb.CreateVector(data));
auto message =
CreateGcsTableEntry(fbb, GcsTableNotificationMode::APPEND_OR_ADD,
RedisStringToFlatbuf(fbb, entry_id), fbb.CreateVector(data));
fbb.Finish(message);
} break;
case REDISMODULE_KEYTYPE_EMPTY: {
auto message = CreateGcsTableEntry(
fbb, RedisStringToFlatbuf(fbb, entry_id),
fbb, GcsTableNotificationMode::APPEND_OR_ADD, RedisStringToFlatbuf(fbb, entry_id),
fbb.CreateVector(std::vector<flatbuffers::Offset<flatbuffers::String>>()));
fbb.Finish(message);
} break;
Expand Down Expand Up @@ -752,6 +870,8 @@ int DebugString_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int
// Wrap all Redis commands with Redis' auto memory management.
AUTO_MEMORY(TableAdd_RedisCommand);
AUTO_MEMORY(TableAppend_RedisCommand);
AUTO_MEMORY(SetAdd_RedisCommand);
AUTO_MEMORY(SetRemove_RedisCommand);
AUTO_MEMORY(TableLookup_RedisCommand);
AUTO_MEMORY(TableRequestNotifications_RedisCommand);
AUTO_MEMORY(TableDelete_RedisCommand);
Expand Down Expand Up @@ -785,6 +905,16 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_ERR;
}

if (RedisModule_CreateCommand(ctx, "ray.set_add", SetAdd_RedisCommand, "write", 0, 0,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should be "write pubsub", right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Seems you are right. But why RAY.TABLE_APPEND is write?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

According to https://redis.io/topics/modules-api-ref , RAY.TABLE_APPEND should be also set to "write pubsub". However, it looks like these commands work fine without pubsub... The document does not explain how it will use strflags, but we should keep it right.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I've updated RAY.TABLE_APPEND, RAY.SET_ADD and RAY.SET_REMOVE to write pubsub flag.

0) == REDISMODULE_ERR) {
return REDISMODULE_ERR;
}

if (RedisModule_CreateCommand(ctx, "ray.set_remove", SetRemove_RedisCommand, "write", 0,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The same, should be "write pubsub", right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

done

0, 0) == REDISMODULE_ERR) {
return REDISMODULE_ERR;
}

if (RedisModule_CreateCommand(ctx, "ray.table_lookup", TableLookup_RedisCommand,
"readonly", 0, 0, 0) == REDISMODULE_ERR) {
return REDISMODULE_ERR;
Expand Down
61 changes: 60 additions & 1 deletion src/ray/gcs/tables.cc
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,20 @@ template <typename ID, typename Data>
Status Log<ID, Data>::Subscribe(const JobID &job_id, const ClientID &client_id,
const Callback &subscribe,
const SubscriptionCallback &done) {
auto subscribeWrapper = [subscribe](AsyncGcsClient *client, const ID &id,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

subscribeWrapper seems not consistent with the code style. May be subscribe_wrapper?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

thx!

const GcsTableNotificationMode mode,
const std::vector<DataT> &data) {
RAY_CHECK(mode != GcsTableNotificationMode::REMOVE);
subscribe(client, id, data);
};
return Subscribe(job_id, client_id, subscribeWrapper, done);
}

template <typename ID, typename Data>
Status Log<ID, Data>::Subscribe(const JobID &job_id,
const ClientID &client_id,
const NotificationCallback &subscribe,
const SubscriptionCallback &done) {
RAY_CHECK(subscribe_callback_index_ == -1)
<< "Client called Subscribe twice on the same table";
auto callback = [this, subscribe, done](const std::string &data) {
Expand All @@ -137,7 +151,7 @@ Status Log<ID, Data>::Subscribe(const JobID &job_id, const ClientID &client_id,
data_root->UnPackTo(&result);
results.emplace_back(std::move(result));
}
subscribe(client_, id, results);
subscribe(client_, id, root->mode(), results);
}
}
// We do not delete the callback after calling it since there may be
Expand Down Expand Up @@ -274,6 +288,50 @@ std::string Table<ID, Data>::DebugString() const {
return result.str();
}

template <typename ID, typename Data>
Status Set<ID, Data>::Add(const JobID &job_id, const ID &id,
std::shared_ptr<DataT> &dataT, const WriteCallback &done) {
num_adds_++;
auto callback = [this, id, dataT, done](const std::string &data) {
if (done != nullptr) {
(done)(client_, id, *dataT);
}
return true;
};
flatbuffers::FlatBufferBuilder fbb;
fbb.ForceDefaults(true);
fbb.Finish(Data::Pack(fbb, dataT.get()));
return GetRedisContext(id)->RunAsync("RAY.SET_ADD", id, fbb.GetBufferPointer(),
fbb.GetSize(), prefix_, pubsub_channel_,
std::move(callback));
}

template <typename ID, typename Data>
Status Set<ID, Data>::Remove(const JobID &job_id, const ID &id,
std::shared_ptr<DataT> &dataT, const WriteCallback &done) {
num_removes_++;
auto callback = [this, id, dataT, done](const std::string &data) {
if (done != nullptr) {
(done)(client_, id, *dataT);
}
return true;
};
flatbuffers::FlatBufferBuilder fbb;
fbb.ForceDefaults(true);
fbb.Finish(Data::Pack(fbb, dataT.get()));
return GetRedisContext(id)->RunAsync("RAY.SET_REMOVE", id, fbb.GetBufferPointer(),
fbb.GetSize(), prefix_, pubsub_channel_,
std::move(callback));
}

template <typename ID, typename Data>
std::string Set<ID, Data>::DebugString() const {
std::stringstream result;
result << "num lookups: " << num_lookups_ << ", num adds: " << num_adds_
<< ", num removes: " << num_removes_;
return result.str();
}

Status ErrorTable::PushErrorToDriver(const JobID &job_id, const std::string &type,
const std::string &error_message, double timestamp) {
auto data = std::make_shared<ErrorTableDataT>();
Expand Down Expand Up @@ -508,6 +566,7 @@ Status ActorCheckpointIdTable::AddCheckpointId(const JobID &job_id,
}

template class Log<ObjectID, ObjectTableData>;
template class Set<ObjectID, ObjectTableData>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

template class Log<ObjectID, ObjectTableData>; isn't needed any more, right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

If I remove this line of code, I'll get the following build error:

Undefined symbols for architecture x86_64:
  "ray::gcs::Log<ray::UniqueID, ObjectTableData>::Delete(ray::UniqueID const&, std::__1::vector<ray::UniqueID, std::__1::allocator<ray::UniqueID> > const&)", referenced from:
      ray::gcs::TestDeleteKeysFromSet(ray::UniqueID const&, std::__1::shared_ptr<ray::gcs::AsyncGcsClient>, std::__1::vector<std::__1::shared_ptr<ObjectTableDataT>, std::__1::allocator<std::__1::shared_ptr<ObjectTableDataT> > >&) in client_test.cc.o
  "ray::gcs::Log<ray::UniqueID, ObjectTableData>::Delete(ray::UniqueID const&, ray::UniqueID const&)", referenced from:
      ray::gcs::TestDeleteKeysFromSet(ray::UniqueID const&, std::__1::shared_ptr<ray::gcs::AsyncGcsClient>, std::__1::vector<std::__1::shared_ptr<ObjectTableDataT>, std::__1::allocator<std::__1::shared_ptr<ObjectTableDataT> > >&) in client_test.cc.o
  "ray::gcs::Log<ray::UniqueID, ObjectTableData>::Lookup(ray::UniqueID const&, ray::UniqueID const&, std::__1::function<void (ray::gcs::AsyncGcsClient*, ray::UniqueID const&, std::__1::vector<ObjectTableDataT, std::__1::allocator<ObjectTableDataT> > const&)> const&)", referenced from:
      ray::gcs::TestSet(ray::UniqueID const&, std::__1::shared_ptr<ray::gcs::AsyncGcsClient>) in client_test.cc.o
      ray::gcs::TestDeleteKeysFromSet(ray::UniqueID const&, std::__1::shared_ptr<ray::gcs::AsyncGcsClient>, std::__1::vector<std::__1::shared_ptr<ObjectTableDataT>, std::__1::allocator<std::__1::shared_ptr<ObjectTableDataT> > >&) in client_test.cc.o
ld: symbol(s) not found for architecture x86_64

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah yeah, I think this is just because Set inherits from Log. :(

template class Log<TaskID, ray::protocol::Task>;
template class Table<TaskID, ray::protocol::Task>;
template class Table<TaskID, TaskTableData>;
Expand Down
Loading