mirror of
https://github.com/wassname/ray.git
synced 2026-09-10 12:38:43 +08:00
Treat actor creation like a regular task. (#1668)
* Treat actor creation like a regular task. * Small cleanups. * Change semantics of actor resource handling. * Bug fix. * Minor linting * Bug fix * Fix jenkins test. * Fix actor tests * Some cleanups * Bug fix * Fix bug. * Remove cached actor tasks when a driver is removed. * Add more info to taskspec in global state API. * Fix cyclic import bug in tune. * Fix * Fix linting. * Fix linting. * Don't schedule any tasks (especially actor creaiton tasks) on local schedulers with 0 CPUs. * Bug fix. * Add test for 0 CPU case * Fix linting * Address comments. * Fix typos and add comment. * Add assertion and fix test.
This commit is contained in:
committed by
Stephanie Wang
parent
3c080f4baa
commit
96913be939
@@ -29,6 +29,10 @@ table TaskInfo {
|
||||
parent_task_id: string;
|
||||
// A count of the number of tasks submitted by the parent task before this one.
|
||||
parent_counter: int;
|
||||
// The ID of the actor to create if this is an actor creation task.
|
||||
actor_creation_id: string;
|
||||
// The dummy object ID of the actor creation task if this is an actor method.
|
||||
actor_creation_dummy_object_id: string;
|
||||
// Actor ID of the task. This is the actor that this task is executed on
|
||||
// or NIL_ACTOR_ID if the task is just a normal task.
|
||||
actor_id: string;
|
||||
@@ -162,3 +166,12 @@ table DriverTableMessage {
|
||||
// The driver ID of the driver that died.
|
||||
driver_id: string;
|
||||
}
|
||||
|
||||
table ActorCreationNotification {
|
||||
// The ID of the actor that was created.
|
||||
actor_id: string;
|
||||
// The ID of the driver that created the actor.
|
||||
driver_id: string;
|
||||
// The ID of the local scheduler that created the actor.
|
||||
local_scheduler_id: string;
|
||||
}
|
||||
|
||||
@@ -272,9 +272,9 @@ static int PyTask_init(PyTask *self, PyObject *args, PyObject *kwds) {
|
||||
/* ID of the driver that this task originates from. */
|
||||
UniqueID driver_id;
|
||||
/* ID of the actor this task should run on. */
|
||||
UniqueID actor_id = UniqueID::nil();
|
||||
UniqueID actor_id = ActorID::nil();
|
||||
/* ID of the actor handle used to submit this task. */
|
||||
UniqueID actor_handle_id = UniqueID::nil();
|
||||
UniqueID actor_handle_id = ActorHandleID::nil();
|
||||
/* How many tasks have been launched on the actor so far? */
|
||||
int actor_counter = 0;
|
||||
/* True if this is an actor checkpoint task and false otherwise. */
|
||||
@@ -289,15 +289,21 @@ static int PyTask_init(PyTask *self, PyObject *args, PyObject *kwds) {
|
||||
TaskID parent_task_id;
|
||||
/* The number of tasks that the parent task has called prior to this one. */
|
||||
int parent_counter;
|
||||
// The actor creation ID.
|
||||
ActorID actor_creation_id = ActorID::nil();
|
||||
// The dummy object for the actor creation task (if this is an actor method).
|
||||
ObjectID actor_creation_dummy_object_id = ObjectID::nil();
|
||||
/* Arguments of the task that are execution-dependent. These must be
|
||||
* PyObjectIDs). */
|
||||
PyObject *execution_arguments = NULL;
|
||||
/* Dictionary of resource requirements for this task. */
|
||||
PyObject *resource_map = NULL;
|
||||
if (!PyArg_ParseTuple(args, "O&O&OiO&i|O&O&iOOO", &PyObjectToUniqueID,
|
||||
if (!PyArg_ParseTuple(args, "O&O&OiO&i|O&O&O&O&iOOO", &PyObjectToUniqueID,
|
||||
&driver_id, &PyObjectToUniqueID, &function_id,
|
||||
&arguments, &num_returns, &PyObjectToUniqueID,
|
||||
&parent_task_id, &parent_counter, &PyObjectToUniqueID,
|
||||
&actor_creation_id, &PyObjectToUniqueID,
|
||||
&actor_creation_dummy_object_id, &PyObjectToUniqueID,
|
||||
&actor_id, &PyObjectToUniqueID, &actor_handle_id,
|
||||
&actor_counter, &is_actor_checkpoint_method_object,
|
||||
&execution_arguments, &resource_map)) {
|
||||
@@ -312,10 +318,11 @@ static int PyTask_init(PyTask *self, PyObject *args, PyObject *kwds) {
|
||||
|
||||
Py_ssize_t size = PyList_Size(arguments);
|
||||
/* Construct the task specification. */
|
||||
TaskSpec_start_construct(g_task_builder, driver_id, parent_task_id,
|
||||
parent_counter, actor_id, actor_handle_id,
|
||||
actor_counter, is_actor_checkpoint_method,
|
||||
function_id, num_returns);
|
||||
TaskSpec_start_construct(
|
||||
g_task_builder, driver_id, parent_task_id, parent_counter,
|
||||
actor_creation_id, actor_creation_dummy_object_id, actor_id,
|
||||
actor_handle_id, actor_counter, is_actor_checkpoint_method, function_id,
|
||||
num_returns);
|
||||
/* Add the task arguments. */
|
||||
for (Py_ssize_t i = 0; i < size; ++i) {
|
||||
PyObject *arg = PyList_GetItem(arguments, i);
|
||||
@@ -463,6 +470,21 @@ static PyObject *PyTask_arguments(PyObject *self) {
|
||||
return arg_list;
|
||||
}
|
||||
|
||||
static PyObject *PyTask_actor_creation_id(PyObject *self) {
|
||||
ActorID actor_creation_id =
|
||||
TaskSpec_actor_creation_id(((PyTask *) self)->spec);
|
||||
return PyObjectID_make(actor_creation_id);
|
||||
}
|
||||
|
||||
static PyObject *PyTask_actor_creation_dummy_object_id(PyObject *self) {
|
||||
ActorID actor_creation_dummy_object_id = ActorID::nil();
|
||||
if (TaskSpec_is_actor_task(((PyTask *) self)->spec)) {
|
||||
actor_creation_dummy_object_id =
|
||||
TaskSpec_actor_creation_dummy_object_id(((PyTask *) self)->spec);
|
||||
}
|
||||
return PyObjectID_make(actor_creation_dummy_object_id);
|
||||
}
|
||||
|
||||
static PyObject *PyTask_required_resources(PyObject *self) {
|
||||
TaskSpec *task = ((PyTask *) self)->spec;
|
||||
PyObject *required_resources = PyDict_New();
|
||||
@@ -520,6 +542,11 @@ static PyMethodDef PyTask_methods[] = {
|
||||
"Return the task ID for this task."},
|
||||
{"arguments", (PyCFunction) PyTask_arguments, METH_NOARGS,
|
||||
"Return the arguments for the task."},
|
||||
{"actor_creation_id", (PyCFunction) PyTask_actor_creation_id, METH_NOARGS,
|
||||
"Return the actor creation ID for the task."},
|
||||
{"actor_creation_dummy_object_id",
|
||||
(PyCFunction) PyTask_actor_creation_dummy_object_id, METH_NOARGS,
|
||||
"Return the actor creation dummy object ID for the task."},
|
||||
{"required_resources", (PyCFunction) PyTask_required_resources, METH_NOARGS,
|
||||
"Return the resource vector of the task."},
|
||||
{"returns", (PyCFunction) PyTask_returns, METH_NOARGS,
|
||||
|
||||
@@ -1,6 +1,31 @@
|
||||
#include "actor_notification_table.h"
|
||||
|
||||
#include "common_protocol.h"
|
||||
#include "redis.h"
|
||||
|
||||
void publish_actor_creation_notification(DBHandle *db_handle,
|
||||
const ActorID &actor_id,
|
||||
const WorkerID &driver_id,
|
||||
const DBClientID &local_scheduler_id) {
|
||||
// Create a flatbuffer object to serialize and publish.
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
// Create the flatbuffers message.
|
||||
auto message = CreateActorCreationNotification(
|
||||
fbb, to_flatbuf(fbb, actor_id), to_flatbuf(fbb, driver_id),
|
||||
to_flatbuf(fbb, local_scheduler_id));
|
||||
fbb.Finish(message);
|
||||
|
||||
ActorCreationNotificationData *data =
|
||||
(ActorCreationNotificationData *) malloc(
|
||||
sizeof(ActorCreationNotificationData) + fbb.GetSize());
|
||||
data->size = fbb.GetSize();
|
||||
memcpy(&data->flatbuffer_data[0], fbb.GetBufferPointer(), fbb.GetSize());
|
||||
|
||||
init_table_callback(db_handle, UniqueID::nil(), __func__,
|
||||
new CommonCallbackData(data), NULL, NULL,
|
||||
redis_publish_actor_creation_notification, NULL);
|
||||
}
|
||||
|
||||
void actor_notification_table_subscribe(
|
||||
DBHandle *db_handle,
|
||||
actor_notification_table_subscribe_callback subscribe_callback,
|
||||
|
||||
@@ -11,12 +11,33 @@
|
||||
|
||||
/* Callback for subscribing to the local scheduler table. */
|
||||
typedef void (*actor_notification_table_subscribe_callback)(
|
||||
ActorID actor_id,
|
||||
WorkerID driver_id,
|
||||
DBClientID local_scheduler_id,
|
||||
bool reconstruct,
|
||||
const ActorID &actor_id,
|
||||
const WorkerID &driver_id,
|
||||
const DBClientID &local_scheduler_id,
|
||||
void *user_context);
|
||||
|
||||
/// Publish an actor creation notification. This is published by a local
|
||||
/// scheduler once it creates an actor.
|
||||
///
|
||||
/// \param db_handle Database handle.
|
||||
/// \param actor_id The ID of the actor that was created.
|
||||
/// \param driver_id The ID of the driver that created the actor.
|
||||
/// \param local_scheduler_id The ID of the local scheduler that created the
|
||||
/// actor.
|
||||
/// \return Void.
|
||||
void publish_actor_creation_notification(DBHandle *db_handle,
|
||||
const ActorID &actor_id,
|
||||
const WorkerID &driver_id,
|
||||
const DBClientID &local_scheduler_id);
|
||||
|
||||
/// Data that is needed to publish an actor creation notification.
|
||||
typedef struct {
|
||||
/// The size of the flatbuffer object.
|
||||
int64_t size;
|
||||
/// The information to be sent.
|
||||
uint8_t flatbuffer_data[0];
|
||||
} ActorCreationNotificationData;
|
||||
|
||||
/**
|
||||
* Register a callback to process actor notification events.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "common_protocol.h"
|
||||
#include "local_scheduler_table.h"
|
||||
|
||||
#include "common_protocol.h"
|
||||
#include "redis.h"
|
||||
|
||||
void local_scheduler_table_subscribe(
|
||||
|
||||
+46
-32
@@ -1033,7 +1033,8 @@ void redis_task_table_test_and_update_callback(redisAsyncContext *c,
|
||||
* delayed when added to the task table if they are submitted to a local
|
||||
* scheduler before it receives the notification that maps the actor to a
|
||||
* local scheduler. */
|
||||
RAY_LOG(ERROR) << "No task found during task_table_test_and_update";
|
||||
RAY_LOG(ERROR) << "No task found during task_table_test_and_update for "
|
||||
<< "task with ID " << callback_data->id;
|
||||
return;
|
||||
}
|
||||
/* Determine whether the update happened. */
|
||||
@@ -1541,6 +1542,40 @@ void redis_plasma_manager_send_heartbeat(TableCallbackData *callback_data) {
|
||||
destroy_timer_callback(db->loop, callback_data);
|
||||
}
|
||||
|
||||
void redis_publish_actor_creation_notification_callback(redisAsyncContext *c,
|
||||
void *r,
|
||||
void *privdata) {
|
||||
REDIS_CALLBACK_HEADER(db, callback_data, r);
|
||||
|
||||
redisReply *reply = (redisReply *) r;
|
||||
RAY_CHECK(reply->type == REDIS_REPLY_INTEGER);
|
||||
RAY_LOG(DEBUG) << reply->integer << " subscribers received this publish.";
|
||||
// At the very least, the local scheduler that publishes this message should
|
||||
// also receive it.
|
||||
RAY_CHECK(reply->integer >= 1);
|
||||
|
||||
RAY_CHECK(callback_data->done_callback == NULL);
|
||||
// Clean up the timer and callback.
|
||||
destroy_timer_callback(db->loop, callback_data);
|
||||
}
|
||||
|
||||
void redis_publish_actor_creation_notification(
|
||||
TableCallbackData *callback_data) {
|
||||
DBHandle *db = callback_data->db_handle;
|
||||
|
||||
ActorCreationNotificationData *data =
|
||||
(ActorCreationNotificationData *) callback_data->data->Get();
|
||||
|
||||
int status = redisAsyncCommand(
|
||||
db->context, redis_publish_actor_creation_notification_callback,
|
||||
(void *) callback_data->timer_id, "PUBLISH actor_notifications %b",
|
||||
&data->flatbuffer_data[0], data->size);
|
||||
if ((status == REDIS_ERR) || db->context->err) {
|
||||
LOG_REDIS_DEBUG(db->context,
|
||||
"error in redis_publish_actor_creation_notification");
|
||||
}
|
||||
}
|
||||
|
||||
void redis_actor_notification_table_subscribe_callback(redisAsyncContext *c,
|
||||
void *r,
|
||||
void *privdata) {
|
||||
@@ -1554,43 +1589,22 @@ void redis_actor_notification_table_subscribe_callback(redisAsyncContext *c,
|
||||
<< message_type->str;
|
||||
|
||||
if (strcmp(message_type->str, "message") == 0) {
|
||||
/* Handle an actor notification message. Parse the payload and call the
|
||||
* subscribe callback. */
|
||||
// Handle an actor notification message. Parse the payload and call the
|
||||
// subscribe callback.
|
||||
redisReply *payload = reply->element[2];
|
||||
ActorNotificationTableSubscribeData *data =
|
||||
(ActorNotificationTableSubscribeData *) callback_data->data->Get();
|
||||
/* The payload should be the concatenation of three IDs. */
|
||||
ActorID actor_id;
|
||||
WorkerID driver_id;
|
||||
DBClientID local_scheduler_id;
|
||||
bool reconstruct;
|
||||
RAY_CHECK(sizeof(actor_id) + sizeof(driver_id) +
|
||||
sizeof(local_scheduler_id) + 1 ==
|
||||
payload->len);
|
||||
char *current_ptr = payload->str;
|
||||
/* Parse the actor ID. */
|
||||
memcpy(&actor_id, current_ptr, sizeof(actor_id));
|
||||
current_ptr += sizeof(actor_id);
|
||||
/* Parse the driver ID. */
|
||||
memcpy(&driver_id, current_ptr, sizeof(driver_id));
|
||||
current_ptr += sizeof(driver_id);
|
||||
/* Parse the local scheduler ID. */
|
||||
memcpy(&local_scheduler_id, current_ptr, sizeof(local_scheduler_id));
|
||||
current_ptr += sizeof(local_scheduler_id);
|
||||
/* Parse the reconstruct bit. */
|
||||
if (*current_ptr == '1') {
|
||||
reconstruct = true;
|
||||
} else if (*current_ptr == '0') {
|
||||
reconstruct = false;
|
||||
} else {
|
||||
reconstruct = false; // We set this value to avoid a compiler warning.
|
||||
RAY_LOG(FATAL) << "This code should be unreachable.";
|
||||
}
|
||||
current_ptr += 1;
|
||||
|
||||
auto message =
|
||||
flatbuffers::GetRoot<ActorCreationNotification>(payload->str);
|
||||
ActorID actor_id = from_flatbuf(*message->actor_id());
|
||||
WorkerID driver_id = from_flatbuf(*message->driver_id());
|
||||
DBClientID local_scheduler_id =
|
||||
from_flatbuf(*message->local_scheduler_id());
|
||||
|
||||
if (data->subscribe_callback) {
|
||||
data->subscribe_callback(actor_id, driver_id, local_scheduler_id,
|
||||
reconstruct, data->subscribe_context);
|
||||
data->subscribe_context);
|
||||
}
|
||||
} else if (strcmp(message_type->str, "subscribe") == 0) {
|
||||
/* The reply for the initial SUBSCRIBE command. */
|
||||
|
||||
@@ -332,6 +332,14 @@ void redis_plasma_manager_send_heartbeat(TableCallbackData *callback_data);
|
||||
*/
|
||||
void redis_actor_table_mark_removed(DBHandle *db, ActorID actor_id);
|
||||
|
||||
/// Publish an actor creation notification.
|
||||
///
|
||||
/// \param callback_data Data structure containing redis connection and timeout
|
||||
/// information.
|
||||
/// \return Void.
|
||||
void redis_publish_actor_creation_notification(
|
||||
TableCallbackData *callback_data);
|
||||
|
||||
/**
|
||||
* Subscribe to updates about newly created actors.
|
||||
*
|
||||
|
||||
+36
-4
@@ -37,8 +37,10 @@ class TaskBuilder {
|
||||
void Start(UniqueID driver_id,
|
||||
TaskID parent_task_id,
|
||||
int64_t parent_counter,
|
||||
ActorID actor_creation_id,
|
||||
ObjectID actor_creation_dummy_object_id,
|
||||
ActorID actor_id,
|
||||
ActorID actor_handle_id,
|
||||
ActorHandleID actor_handle_id,
|
||||
int64_t actor_counter,
|
||||
bool is_actor_checkpoint_method,
|
||||
FunctionID function_id,
|
||||
@@ -46,6 +48,8 @@ class TaskBuilder {
|
||||
driver_id_ = driver_id;
|
||||
parent_task_id_ = parent_task_id;
|
||||
parent_counter_ = parent_counter;
|
||||
actor_creation_id_ = actor_creation_id;
|
||||
actor_creation_dummy_object_id_ = actor_creation_dummy_object_id;
|
||||
actor_id_ = actor_id;
|
||||
actor_handle_id_ = actor_handle_id;
|
||||
actor_counter_ = actor_counter;
|
||||
@@ -58,6 +62,9 @@ class TaskBuilder {
|
||||
sha256_update(&ctx, (BYTE *) &driver_id, sizeof(driver_id));
|
||||
sha256_update(&ctx, (BYTE *) &parent_task_id, sizeof(parent_task_id));
|
||||
sha256_update(&ctx, (BYTE *) &parent_counter, sizeof(parent_counter));
|
||||
sha256_update(&ctx, (BYTE *) &actor_creation_id, sizeof(actor_creation_id));
|
||||
sha256_update(&ctx, (BYTE *) &actor_creation_dummy_object_id,
|
||||
sizeof(actor_creation_dummy_object_id));
|
||||
sha256_update(&ctx, (BYTE *) &actor_id, sizeof(actor_id));
|
||||
sha256_update(&ctx, (BYTE *) &actor_counter, sizeof(actor_counter));
|
||||
sha256_update(&ctx, (BYTE *) &is_actor_checkpoint_method,
|
||||
@@ -103,6 +110,8 @@ class TaskBuilder {
|
||||
auto message = CreateTaskInfo(
|
||||
fbb, to_flatbuf(fbb, driver_id_), to_flatbuf(fbb, task_id),
|
||||
to_flatbuf(fbb, parent_task_id_), parent_counter_,
|
||||
to_flatbuf(fbb, actor_creation_id_),
|
||||
to_flatbuf(fbb, actor_creation_dummy_object_id_),
|
||||
to_flatbuf(fbb, actor_id_), to_flatbuf(fbb, actor_handle_id_),
|
||||
actor_counter_, is_actor_checkpoint_method_,
|
||||
to_flatbuf(fbb, function_id_), arguments, fbb.CreateVector(returns),
|
||||
@@ -127,6 +136,8 @@ class TaskBuilder {
|
||||
UniqueID driver_id_;
|
||||
TaskID parent_task_id_;
|
||||
int64_t parent_counter_;
|
||||
ActorID actor_creation_id_;
|
||||
ObjectID actor_creation_dummy_object_id_;
|
||||
ActorID actor_id_;
|
||||
ActorID actor_handle_id_;
|
||||
int64_t actor_counter_;
|
||||
@@ -170,15 +181,18 @@ void TaskSpec_start_construct(TaskBuilder *builder,
|
||||
UniqueID driver_id,
|
||||
TaskID parent_task_id,
|
||||
int64_t parent_counter,
|
||||
ActorID actor_creation_id,
|
||||
ObjectID actor_creation_dummy_object_id,
|
||||
ActorID actor_id,
|
||||
ActorID actor_handle_id,
|
||||
int64_t actor_counter,
|
||||
bool is_actor_checkpoint_method,
|
||||
FunctionID function_id,
|
||||
int64_t num_returns) {
|
||||
builder->Start(driver_id, parent_task_id, parent_counter, actor_id,
|
||||
actor_handle_id, actor_counter, is_actor_checkpoint_method,
|
||||
function_id, num_returns);
|
||||
builder->Start(driver_id, parent_task_id, parent_counter, actor_creation_id,
|
||||
actor_creation_dummy_object_id, actor_id, actor_handle_id,
|
||||
actor_counter, is_actor_checkpoint_method, function_id,
|
||||
num_returns);
|
||||
}
|
||||
|
||||
TaskSpec *TaskSpec_finish_construct(TaskBuilder *builder, int64_t *size) {
|
||||
@@ -233,6 +247,24 @@ bool TaskSpec_is_actor_task(TaskSpec *spec) {
|
||||
return !TaskSpec_actor_id(spec).is_nil();
|
||||
}
|
||||
|
||||
ActorID TaskSpec_actor_creation_id(TaskSpec *spec) {
|
||||
RAY_CHECK(spec);
|
||||
auto message = flatbuffers::GetRoot<TaskInfo>(spec);
|
||||
return from_flatbuf(*message->actor_creation_id());
|
||||
}
|
||||
|
||||
ObjectID TaskSpec_actor_creation_dummy_object_id(TaskSpec *spec) {
|
||||
RAY_CHECK(spec);
|
||||
// The task must be an actor method.
|
||||
RAY_CHECK(TaskSpec_is_actor_task(spec));
|
||||
auto message = flatbuffers::GetRoot<TaskInfo>(spec);
|
||||
return from_flatbuf(*message->actor_creation_dummy_object_id());
|
||||
}
|
||||
|
||||
bool TaskSpec_is_actor_creation_task(TaskSpec *spec) {
|
||||
return !TaskSpec_actor_creation_id(spec).is_nil();
|
||||
}
|
||||
|
||||
int64_t TaskSpec_actor_counter(TaskSpec *spec) {
|
||||
RAY_CHECK(spec);
|
||||
auto message = flatbuffers::GetRoot<TaskInfo>(spec);
|
||||
|
||||
+33
-5
@@ -190,6 +190,9 @@ void free_task_builder(TaskBuilder *builder);
|
||||
* @param parent_task_id The task ID of the task that submitted this task.
|
||||
* @param parent_counter A counter indicating how many tasks were submitted by
|
||||
* the parent task prior to this one.
|
||||
* @param actor_creation_id The actor creation ID of this task.
|
||||
* @param actor_creation_dummy_object_id The dummy object for the corresponding
|
||||
* actor creation task, assuming this is an actor method.
|
||||
* @param actor_id The ID of the actor that this task is for. If it is not an
|
||||
* actor task, then this if NIL_ACTOR_ID.
|
||||
* @param actor_handle_id The ID of the actor handle that this task was
|
||||
@@ -210,8 +213,10 @@ void TaskSpec_start_construct(TaskBuilder *B,
|
||||
UniqueID driver_id,
|
||||
TaskID parent_task_id,
|
||||
int64_t parent_counter,
|
||||
UniqueID actor_id,
|
||||
UniqueID actor_handle_id,
|
||||
ActorID actor_creation_id,
|
||||
ObjectID actor_creation_dummy_object_id,
|
||||
ActorID actor_id,
|
||||
ActorHandleID actor_handle_id,
|
||||
int64_t actor_counter,
|
||||
bool is_actor_checkpoint_method,
|
||||
FunctionID function_id,
|
||||
@@ -241,7 +246,7 @@ FunctionID TaskSpec_function(TaskSpec *spec);
|
||||
* @param spec The task_spec in question.
|
||||
* @return The actor ID of the actor the task is part of.
|
||||
*/
|
||||
UniqueID TaskSpec_actor_id(TaskSpec *spec);
|
||||
ActorID TaskSpec_actor_id(TaskSpec *spec);
|
||||
|
||||
/**
|
||||
* Return the actor handle ID of the task.
|
||||
@@ -249,7 +254,7 @@ UniqueID TaskSpec_actor_id(TaskSpec *spec);
|
||||
* @param spec The task_spec in question.
|
||||
* @return The ID of the actor handle that the task was submitted through.
|
||||
*/
|
||||
UniqueID TaskSpec_actor_handle_id(TaskSpec *spec);
|
||||
ActorID TaskSpec_actor_handle_id(TaskSpec *spec);
|
||||
|
||||
/**
|
||||
* Return whether this task is for an actor.
|
||||
@@ -259,6 +264,26 @@ UniqueID TaskSpec_actor_handle_id(TaskSpec *spec);
|
||||
*/
|
||||
bool TaskSpec_is_actor_task(TaskSpec *spec);
|
||||
|
||||
/// Return whether this task is an actor creation task or not.
|
||||
///
|
||||
/// \param spec The task_spec in question.
|
||||
/// \return True if this task is an actor creation task and false otherwise.
|
||||
bool TaskSpec_is_actor_creation_task(TaskSpec *spec);
|
||||
|
||||
/// Return the actor creation ID of the task. The task must be an actor creation
|
||||
/// task.
|
||||
///
|
||||
/// \param spec The task_spec in question.
|
||||
/// \return The actor creation ID if this is an actor creation task.
|
||||
ActorID TaskSpec_actor_creation_id(TaskSpec *spec);
|
||||
|
||||
/// Return the actor creation dummy object ID of the task. The task must be an
|
||||
/// actor task.
|
||||
///
|
||||
/// \param spec The task_spec in question.
|
||||
/// \return The actor creation dummy object ID corresponding to this actor task.
|
||||
ObjectID TaskSpec_actor_creation_dummy_object_id(TaskSpec *spec);
|
||||
|
||||
/**
|
||||
* Return the actor counter of the task. This starts at 0 and increments by 1
|
||||
* every time a new task is submitted to run on the actor.
|
||||
@@ -508,7 +533,10 @@ typedef enum {
|
||||
/** The task was not able to finish. */
|
||||
TASK_STATUS_LOST = 32,
|
||||
/** The task will be submitted for reexecution. */
|
||||
TASK_STATUS_RECONSTRUCTING = 64
|
||||
TASK_STATUS_RECONSTRUCTING = 64,
|
||||
/** An actor task is cached at a local scheduler and is waiting for the
|
||||
* corresponding actor to be created. */
|
||||
TASK_STATUS_ACTOR_CACHED = 128
|
||||
} scheduling_state;
|
||||
|
||||
/** A task is an execution of a task specification. It has a state of execution
|
||||
|
||||
@@ -14,8 +14,8 @@ static inline TaskExecutionSpec example_task_execution_spec_with_args(
|
||||
TaskID parent_task_id = TaskID::from_random();
|
||||
FunctionID func_id = FunctionID::from_random();
|
||||
TaskSpec_start_construct(g_task_builder, UniqueID::nil(), parent_task_id, 0,
|
||||
ActorID::nil(), ActorID::nil(), 0, false, func_id,
|
||||
num_returns);
|
||||
ActorID::nil(), ObjectID::nil(), ActorID::nil(),
|
||||
ActorID::nil(), 0, false, func_id, num_returns);
|
||||
for (int64_t i = 0; i < num_args; ++i) {
|
||||
ObjectID arg_id;
|
||||
if (arg_ids == NULL) {
|
||||
|
||||
@@ -16,8 +16,8 @@ TEST task_test(void) {
|
||||
FunctionID func_id = FunctionID::from_random();
|
||||
TaskBuilder *builder = make_task_builder();
|
||||
TaskSpec_start_construct(builder, DriverID::nil(), parent_task_id, 0,
|
||||
ActorID::nil(), ActorID::nil(), 0, false, func_id,
|
||||
2);
|
||||
ActorID::nil(), ObjectID::nil(), ActorID::nil(),
|
||||
ActorID::nil(), 0, false, func_id, 2);
|
||||
|
||||
UniqueID arg1 = UniqueID::from_random();
|
||||
TaskSpec_args_add_ref(builder, &arg1, 1);
|
||||
@@ -56,8 +56,8 @@ TEST deterministic_ids_test(void) {
|
||||
|
||||
/* Construct a first task. */
|
||||
TaskSpec_start_construct(builder, DriverID::nil(), parent_task_id, 0,
|
||||
ActorID::nil(), ActorID::nil(), 0, false, func_id,
|
||||
3);
|
||||
ActorID::nil(), ObjectID::nil(), ActorID::nil(),
|
||||
ActorID::nil(), 0, false, func_id, 3);
|
||||
TaskSpec_args_add_ref(builder, &arg1, 1);
|
||||
TaskSpec_args_add_val(builder, arg2, 11);
|
||||
int64_t size1;
|
||||
@@ -65,8 +65,8 @@ TEST deterministic_ids_test(void) {
|
||||
|
||||
/* Construct a second identical task. */
|
||||
TaskSpec_start_construct(builder, DriverID::nil(), parent_task_id, 0,
|
||||
ActorID::nil(), ActorID::nil(), 0, false, func_id,
|
||||
3);
|
||||
ActorID::nil(), ObjectID::nil(), ActorID::nil(),
|
||||
ActorID::nil(), 0, false, func_id, 3);
|
||||
TaskSpec_args_add_ref(builder, &arg1, 1);
|
||||
TaskSpec_args_add_val(builder, arg2, 11);
|
||||
int64_t size2;
|
||||
@@ -86,8 +86,8 @@ TEST deterministic_ids_test(void) {
|
||||
|
||||
/* Construct a task with a different parent task ID. */
|
||||
TaskSpec_start_construct(builder, DriverID::nil(), TaskID::from_random(), 0,
|
||||
ActorID::nil(), ActorID::nil(), 0, false, func_id,
|
||||
3);
|
||||
ActorID::nil(), ObjectID::nil(), ActorID::nil(),
|
||||
ActorID::nil(), 0, false, func_id, 3);
|
||||
TaskSpec_args_add_ref(builder, &arg1, 1);
|
||||
TaskSpec_args_add_val(builder, arg2, 11);
|
||||
int64_t size3;
|
||||
@@ -95,8 +95,8 @@ TEST deterministic_ids_test(void) {
|
||||
|
||||
/* Construct a task with a different parent counter. */
|
||||
TaskSpec_start_construct(builder, DriverID::nil(), parent_task_id, 1,
|
||||
ActorID::nil(), ActorID::nil(), 0, false, func_id,
|
||||
3);
|
||||
ActorID::nil(), ObjectID::nil(), ActorID::nil(),
|
||||
ActorID::nil(), 0, false, func_id, 3);
|
||||
TaskSpec_args_add_ref(builder, &arg1, 1);
|
||||
TaskSpec_args_add_val(builder, arg2, 11);
|
||||
int64_t size4;
|
||||
@@ -104,8 +104,9 @@ TEST deterministic_ids_test(void) {
|
||||
|
||||
/* Construct a task with a different function ID. */
|
||||
TaskSpec_start_construct(builder, DriverID::nil(), parent_task_id, 0,
|
||||
ActorID::nil(), ActorID::nil(), 0, false,
|
||||
FunctionID::from_random(), 3);
|
||||
ActorID::nil(), ObjectID::nil(), ActorID::nil(),
|
||||
ActorID::nil(), 0, false, FunctionID::from_random(),
|
||||
3);
|
||||
TaskSpec_args_add_ref(builder, &arg1, 1);
|
||||
TaskSpec_args_add_val(builder, arg2, 11);
|
||||
int64_t size5;
|
||||
@@ -113,8 +114,8 @@ TEST deterministic_ids_test(void) {
|
||||
|
||||
/* Construct a task with a different object ID argument. */
|
||||
TaskSpec_start_construct(builder, DriverID::nil(), parent_task_id, 0,
|
||||
ActorID::nil(), ActorID::nil(), 0, false, func_id,
|
||||
3);
|
||||
ActorID::nil(), ObjectID::nil(), ActorID::nil(),
|
||||
ActorID::nil(), 0, false, func_id, 3);
|
||||
ObjectID object_id = ObjectID::from_random();
|
||||
TaskSpec_args_add_ref(builder, &object_id, 1);
|
||||
TaskSpec_args_add_val(builder, arg2, 11);
|
||||
@@ -123,8 +124,8 @@ TEST deterministic_ids_test(void) {
|
||||
|
||||
/* Construct a task with a different value argument. */
|
||||
TaskSpec_start_construct(builder, DriverID::nil(), parent_task_id, 0,
|
||||
ActorID::nil(), ActorID::nil(), 0, false, func_id,
|
||||
3);
|
||||
ActorID::nil(), ObjectID::nil(), ActorID::nil(),
|
||||
ActorID::nil(), 0, false, func_id, 3);
|
||||
TaskSpec_args_add_ref(builder, &arg1, 1);
|
||||
TaskSpec_args_add_val(builder, (uint8_t *) "hello_world", 11);
|
||||
int64_t size7;
|
||||
@@ -168,8 +169,8 @@ TEST send_task(void) {
|
||||
TaskID parent_task_id = TaskID::from_random();
|
||||
FunctionID func_id = FunctionID::from_random();
|
||||
TaskSpec_start_construct(builder, DriverID::nil(), parent_task_id, 0,
|
||||
ActorID::nil(), ActorID::nil(), 0, false, func_id,
|
||||
2);
|
||||
ActorID::nil(), ObjectID::nil(), ActorID::nil(),
|
||||
ActorID::nil(), 0, false, func_id, 2);
|
||||
ObjectID object_id = ObjectID::from_random();
|
||||
TaskSpec_args_add_ref(builder, &object_id, 1);
|
||||
TaskSpec_args_add_val(builder, (uint8_t *) "Hello", 5);
|
||||
|
||||
@@ -24,6 +24,14 @@ void GlobalSchedulerPolicyState_free(GlobalSchedulerPolicyState *policy_state) {
|
||||
*/
|
||||
bool constraints_satisfied_hard(const LocalScheduler *scheduler,
|
||||
const TaskSpec *spec) {
|
||||
if (scheduler->info.static_resources.count("CPU") == 1 &&
|
||||
scheduler->info.static_resources.at("CPU") == 0) {
|
||||
// Don't give tasks to local schedulers that have 0 CPUs. This can be an
|
||||
// issue for actor creation tasks that require 0 CPUs (but the subsequent
|
||||
// actor methods require some CPUs).
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto const &resource_pair : TaskSpec_get_required_resources(spec)) {
|
||||
std::string resource_name = resource_pair.first;
|
||||
double resource_quantity = resource_pair.second;
|
||||
|
||||
@@ -76,17 +76,8 @@ table RegisterClientRequest {
|
||||
is_worker: bool;
|
||||
// The ID of the worker or driver.
|
||||
client_id: string;
|
||||
// The ID of the actor. This is NIL_ACTOR_ID if the worker is not an actor.
|
||||
actor_id: string;
|
||||
// The process ID of this worker.
|
||||
worker_pid: long;
|
||||
// The number of GPUs required by this actor.
|
||||
num_gpus: long;
|
||||
}
|
||||
|
||||
table RegisterClientReply {
|
||||
// The IDs of the GPUs that are reserved for this worker.
|
||||
gpu_ids: [int];
|
||||
}
|
||||
|
||||
table DisconnectClient {
|
||||
|
||||
@@ -226,19 +226,7 @@ void LocalSchedulerState_free(LocalSchedulerState *state) {
|
||||
event_loop_destroy(loop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new worker as a child process.
|
||||
*
|
||||
* @param state The state of the local scheduler.
|
||||
* @return Void.
|
||||
*/
|
||||
void start_worker(LocalSchedulerState *state,
|
||||
ActorID actor_id,
|
||||
bool reconstruct) {
|
||||
/* Non-actors can't be started in reconstruct mode. */
|
||||
if (actor_id.is_nil()) {
|
||||
RAY_CHECK(!reconstruct);
|
||||
}
|
||||
void start_worker(LocalSchedulerState *state) {
|
||||
/* We can't start a worker if we don't have the path to the worker script. */
|
||||
if (state->config.start_worker_command == NULL) {
|
||||
RAY_LOG(DEBUG) << "No valid command to start worker provided. Cannot start "
|
||||
@@ -261,18 +249,6 @@ void start_worker(LocalSchedulerState *state,
|
||||
command_vector.push_back(state->config.start_worker_command[i]);
|
||||
}
|
||||
|
||||
/* Pass in the worker's actor ID. */
|
||||
const char *actor_id_string = "--actor-id";
|
||||
std::string id_string = actor_id.hex();
|
||||
command_vector.push_back(actor_id_string);
|
||||
command_vector.push_back(id_string.c_str());
|
||||
|
||||
/* Add a flag for reconstructing the actor if necessary. */
|
||||
const char *reconstruct_string = "--reconstruct";
|
||||
if (reconstruct) {
|
||||
command_vector.push_back(reconstruct_string);
|
||||
}
|
||||
|
||||
/* Add a NULL pointer to the end. */
|
||||
command_vector.push_back(NULL);
|
||||
|
||||
@@ -419,7 +395,7 @@ LocalSchedulerState *LocalSchedulerState_init(
|
||||
|
||||
/* Start the initial set of workers. */
|
||||
for (int i = 0; i < num_workers; ++i) {
|
||||
start_worker(state, ActorID::nil(), false);
|
||||
start_worker(state);
|
||||
}
|
||||
|
||||
/* Initialize the time at which the previous heartbeat was sent. */
|
||||
@@ -489,9 +465,6 @@ void acquire_resources(
|
||||
RAY_CHECK(state->dynamic_resources[resource_name] >= resource_quantity);
|
||||
}
|
||||
state->dynamic_resources[resource_name] -= resource_quantity;
|
||||
if (resource_name == std::string("CPU")) {
|
||||
RAY_CHECK(worker->resources_in_use[resource_name] == 0);
|
||||
}
|
||||
worker->resources_in_use[resource_name] += resource_quantity;
|
||||
}
|
||||
|
||||
@@ -520,9 +493,6 @@ void release_resources(
|
||||
}
|
||||
|
||||
// Do bookkeeping for general resources types.
|
||||
if (resource_name == std::string("CPU")) {
|
||||
RAY_CHECK(resource_quantity == worker->resources_in_use[resource_name]);
|
||||
}
|
||||
state->dynamic_resources[resource_name] += resource_quantity;
|
||||
worker->resources_in_use[resource_name] -= resource_quantity;
|
||||
}
|
||||
@@ -599,10 +569,44 @@ void assign_task_to_worker(LocalSchedulerState *state,
|
||||
void finish_task(LocalSchedulerState *state, LocalSchedulerClient *worker) {
|
||||
if (worker->task_in_progress != NULL) {
|
||||
TaskSpec *spec = Task_task_execution_spec(worker->task_in_progress)->Spec();
|
||||
/* Return dynamic resources back for the task in progress. */
|
||||
RAY_CHECK(worker->resources_in_use["CPU"] ==
|
||||
TaskSpec_get_required_resource(spec, "CPU"));
|
||||
if (worker->actor_id.is_nil()) {
|
||||
// Return dynamic resources back for the task in progress.
|
||||
if (TaskSpec_is_actor_creation_task(spec)) {
|
||||
// Resources required by the actor creation task are acquired for the
|
||||
// actor's lifetime, so don't return anything here. TODO(rkn): Should the
|
||||
// actor creation task require 1 CPU in addition to any resources acquired
|
||||
// for the lifetime of the actor? If not, then the local scheduler may
|
||||
// schedule an arbitrary number of actor creation tasks concurrently (if
|
||||
// they don't acquire any resources for their entire lifetime). In
|
||||
// practice this will usually be rate-limited by the rate at which we can
|
||||
// create new workers.
|
||||
|
||||
ActorID actor_creation_id = TaskSpec_actor_creation_id(spec);
|
||||
WorkerID driver_id = TaskSpec_driver_id(spec);
|
||||
|
||||
// The driver must be alive because if the driver had been removed, then
|
||||
// this worker would have been killed (because it was executing a task for
|
||||
// the driver).
|
||||
RAY_CHECK(is_driver_alive(state, driver_id));
|
||||
|
||||
// Update the worker struct with this actor ID.
|
||||
RAY_CHECK(worker->actor_id.is_nil());
|
||||
worker->actor_id = actor_creation_id;
|
||||
// Extract the initial execution dependency from the actor creation task.
|
||||
RAY_CHECK(TaskSpec_num_returns(spec) == 1);
|
||||
ObjectID initial_execution_dependency = TaskSpec_return(spec, 0);
|
||||
// Let the scheduling algorithm process the presence of this new worker.
|
||||
handle_convert_worker_to_actor(state, state->algorithm_state,
|
||||
actor_creation_id,
|
||||
initial_execution_dependency, worker);
|
||||
// Publish the actor creation notification. The corresponding callback
|
||||
// handle_actor_creation_callback will update state->actor_mapping.
|
||||
publish_actor_creation_notification(
|
||||
state->db, actor_creation_id, driver_id, get_db_client_id(state->db));
|
||||
} else if (worker->actor_id.is_nil()) {
|
||||
// Return dynamic resources back for the task in progress.
|
||||
RAY_CHECK(worker->resources_in_use["CPU"] ==
|
||||
TaskSpec_get_required_resource(spec, "CPU"));
|
||||
// Return GPU resources.
|
||||
RAY_CHECK(worker->gpus_in_use.size() ==
|
||||
TaskSpec_get_required_resource(spec, "GPU"));
|
||||
release_resources(state, worker, worker->resources_in_use);
|
||||
@@ -610,9 +614,7 @@ void finish_task(LocalSchedulerState *state, LocalSchedulerClient *worker) {
|
||||
// Actor tasks should only specify CPU requirements.
|
||||
RAY_CHECK(0 == TaskSpec_get_required_resource(spec, "GPU"));
|
||||
std::unordered_map<std::string, double> cpu_resources;
|
||||
cpu_resources["CPU"] = worker->resources_in_use["CPU"];
|
||||
std::unordered_map<std::string, double> resources_to_release =
|
||||
worker->resources_in_use;
|
||||
cpu_resources["CPU"] = TaskSpec_get_required_resource(spec, "CPU");
|
||||
release_resources(state, worker, cpu_resources);
|
||||
}
|
||||
/* If we're connected to Redis, update tables. */
|
||||
@@ -902,29 +904,6 @@ void reconstruct_object(LocalSchedulerState *state,
|
||||
reconstruct_object_lookup_callback, (void *) state);
|
||||
}
|
||||
|
||||
void send_client_register_reply(LocalSchedulerState *state,
|
||||
LocalSchedulerClient *worker) {
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
auto message =
|
||||
CreateRegisterClientReply(fbb, fbb.CreateVector(worker->gpus_in_use));
|
||||
fbb.Finish(message);
|
||||
|
||||
/* Send the message to the client. */
|
||||
if (write_message(worker->sock, MessageType_RegisterClientReply,
|
||||
fbb.GetSize(), fbb.GetBufferPointer()) < 0) {
|
||||
if (errno == EPIPE || errno == EBADF || errno == ECONNRESET) {
|
||||
/* Something went wrong, so kill the worker. */
|
||||
kill_worker(state, worker, false, false);
|
||||
RAY_LOG(WARNING) << "Failed to give send register client reply to worker "
|
||||
<< "on fd " << worker->sock
|
||||
<< ". The client may have hung up.";
|
||||
} else {
|
||||
RAY_LOG(FATAL) << "Failed to send register client reply to client on fd "
|
||||
<< worker->sock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_client_register(LocalSchedulerState *state,
|
||||
LocalSchedulerClient *worker,
|
||||
const RegisterClientRequest *message) {
|
||||
@@ -940,40 +919,6 @@ void handle_client_register(LocalSchedulerState *state,
|
||||
/* Update the actor mapping with the actor ID of the worker (if an actor is
|
||||
* running on the worker). */
|
||||
worker->pid = message->worker_pid();
|
||||
ActorID actor_id = from_flatbuf(*message->actor_id());
|
||||
if (!actor_id.is_nil()) {
|
||||
/* Make sure that the local scheduler is aware that it is responsible for
|
||||
* this actor. */
|
||||
RAY_CHECK(state->actor_mapping.count(actor_id) == 1);
|
||||
RAY_CHECK(state->actor_mapping[actor_id].local_scheduler_id ==
|
||||
get_db_client_id(state->db));
|
||||
/* Update the worker struct with this actor ID. */
|
||||
RAY_CHECK(worker->actor_id.is_nil());
|
||||
worker->actor_id = actor_id;
|
||||
/* Let the scheduling algorithm process the presence of this new
|
||||
* worker. */
|
||||
handle_actor_worker_connect(state, state->algorithm_state, actor_id,
|
||||
worker);
|
||||
|
||||
/* If there are enough GPUs available, allocate them and reply to the
|
||||
* actor. */
|
||||
double num_gpus_required = (double) message->num_gpus();
|
||||
|
||||
std::unordered_map<std::string, double> gpu_resources;
|
||||
gpu_resources["GPU"] = num_gpus_required;
|
||||
if (check_dynamic_resources(state, gpu_resources)) {
|
||||
acquire_resources(state, worker, gpu_resources);
|
||||
} else {
|
||||
/* TODO(rkn): This means that an actor wants to register but that there
|
||||
* aren't enough GPUs for it. We should queue this request, and reply to
|
||||
* the actor when GPUs become available. */
|
||||
RAY_LOG(WARNING) << "Attempting to create an actor but there aren't "
|
||||
<< "enough available GPUs. We'll start the worker "
|
||||
<< "anyway without any GPUs, but this is incorrect "
|
||||
<< "behavior.";
|
||||
}
|
||||
}
|
||||
|
||||
/* Register worker process id with the scheduler. */
|
||||
/* Determine if this worker is one of our child processes. */
|
||||
RAY_LOG(DEBUG) << "PID is " << worker->pid;
|
||||
@@ -987,15 +932,6 @@ void handle_client_register(LocalSchedulerState *state,
|
||||
state->child_pids.erase(it);
|
||||
RAY_LOG(DEBUG) << "Found matching child pid " << worker->pid;
|
||||
}
|
||||
|
||||
/* If the worker is an actor that corresponds to a driver that has been
|
||||
* removed, then kill the worker. */
|
||||
if (!actor_id.is_nil()) {
|
||||
WorkerID driver_id = state->actor_mapping[actor_id].driver_id;
|
||||
if (state->removed_drivers.count(driver_id) == 1) {
|
||||
kill_worker(state, worker, false, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Register the driver. Currently we don't do anything here. */
|
||||
}
|
||||
@@ -1164,7 +1100,7 @@ void process_message(event_loop *loop,
|
||||
/* If the disconnected worker was not an actor, start a new worker to make
|
||||
* sure there are enough workers in the pool. */
|
||||
if (worker->actor_id.is_nil()) {
|
||||
start_worker(state, ActorID::nil(), false);
|
||||
start_worker(state);
|
||||
}
|
||||
} break;
|
||||
case MessageType_EventLogMessage: {
|
||||
@@ -1180,7 +1116,6 @@ void process_message(event_loop *loop,
|
||||
case MessageType_RegisterClientRequest: {
|
||||
auto message = flatbuffers::GetRoot<RegisterClientRequest>(input);
|
||||
handle_client_register(state, worker, message);
|
||||
send_client_register_reply(state, worker);
|
||||
} break;
|
||||
case MessageType_GetTask: {
|
||||
/* If this worker reports a completed task, account for resources. */
|
||||
@@ -1360,14 +1295,12 @@ void handle_task_scheduled_callback(Task *original_task,
|
||||
* @param actor_id The ID of the actor being created.
|
||||
* @param local_scheduler_id The ID of the local scheduler that is responsible
|
||||
* for creating the actor.
|
||||
* @param reconstruct True if the actor should be started in "reconstruct" mode.
|
||||
* @param context The context for this callback.
|
||||
* @return Void.
|
||||
*/
|
||||
void handle_actor_creation_callback(ActorID actor_id,
|
||||
WorkerID driver_id,
|
||||
DBClientID local_scheduler_id,
|
||||
bool reconstruct,
|
||||
void handle_actor_creation_callback(const ActorID &actor_id,
|
||||
const WorkerID &driver_id,
|
||||
const DBClientID &local_scheduler_id,
|
||||
void *context) {
|
||||
LocalSchedulerState *state = (LocalSchedulerState *) context;
|
||||
|
||||
@@ -1376,26 +1309,19 @@ void handle_actor_creation_callback(ActorID actor_id,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reconstruct) {
|
||||
/* Make sure the actor entry is not already present in the actor map table.
|
||||
* TODO(rkn): We will need to remove this check to handle the case where the
|
||||
* corresponding publish is retried and the case in which a task that
|
||||
* creates an actor is resubmitted due to fault tolerance. */
|
||||
RAY_CHECK(state->actor_mapping.count(actor_id) == 0);
|
||||
} else {
|
||||
/* In this case, the actor already exists. Check that the driver hasn't
|
||||
* changed but that the local scheduler has. */
|
||||
// TODO(rkn): If we do not have perfect task suppression and it is possible
|
||||
// for a task to be executed simultaneously on two nodes, then we will need to
|
||||
// detect and handle that case.
|
||||
|
||||
if (state->actor_mapping.count(actor_id) != 0) {
|
||||
// This actor already exists.
|
||||
auto it = state->actor_mapping.find(actor_id);
|
||||
RAY_CHECK(it != state->actor_mapping.end());
|
||||
RAY_CHECK(it->second.driver_id == driver_id);
|
||||
RAY_CHECK(!(it->second.local_scheduler_id == local_scheduler_id));
|
||||
/* If the actor was previously assigned to this local scheduler, kill the
|
||||
* actor. */
|
||||
if (it->second.local_scheduler_id == get_db_client_id(state->db)) {
|
||||
/* TODO(rkn): We should kill the actor here if it is still around. Also,
|
||||
* if it hasn't registered yet, we should keep track of its PID so we can
|
||||
* kill it anyway. */
|
||||
/* TODO(swang): Evict actor dummy objects as part of actor cleanup. */
|
||||
// TODO(rkn): The actor was previously assigned to this local scheduler.
|
||||
// We should kill the actor here if it is still around. Also, if it hasn't
|
||||
// registered yet, we should keep track of its PID so we can kill it
|
||||
// anyway.
|
||||
// TODO(swang): Evict actor dummy objects as part of actor cleanup.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1407,15 +1333,9 @@ void handle_actor_creation_callback(ActorID actor_id,
|
||||
entry.driver_id = driver_id;
|
||||
state->actor_mapping[actor_id] = entry;
|
||||
|
||||
/* If this local scheduler is responsible for the actor, then start a new
|
||||
* worker for the actor. */
|
||||
if (local_scheduler_id == get_db_client_id(state->db)) {
|
||||
start_worker(state, actor_id, reconstruct);
|
||||
}
|
||||
/* Let the scheduling algorithm process the fact that a new actor has been
|
||||
* created. */
|
||||
handle_actor_creation_notification(state, state->algorithm_state, actor_id,
|
||||
reconstruct);
|
||||
handle_actor_creation_notification(state, state->algorithm_state, actor_id);
|
||||
}
|
||||
|
||||
int heartbeat_handler(event_loop *loop, timer_id id, void *context) {
|
||||
@@ -1515,6 +1435,12 @@ void start_server(
|
||||
loop, RayConfig::instance()
|
||||
.local_scheduler_reconstruction_timeout_milliseconds(),
|
||||
reconstruct_object_timeout_handler, g_state);
|
||||
// Create a timer for rerunning actor creation tasks for actor tasks that are
|
||||
// cached locally.
|
||||
event_loop_add_timer(
|
||||
loop, RayConfig::instance()
|
||||
.local_scheduler_reconstruction_timeout_milliseconds(),
|
||||
rerun_actor_creation_tasks_timeout_handler, g_state);
|
||||
/* Run event loop. */
|
||||
event_loop_run(loop);
|
||||
}
|
||||
|
||||
@@ -104,15 +104,9 @@ void kill_worker(LocalSchedulerState *state,
|
||||
* scheduler.
|
||||
*
|
||||
* @param state The local scheduler state.
|
||||
* @param actor_id The ID of the actor for this worker. If this worker is not an
|
||||
* actor, then NIL_ACTOR_ID should be used.
|
||||
* @param reconstruct True if the worker is an actor and is being started in
|
||||
* reconstruct mode.
|
||||
* @param Void.
|
||||
*/
|
||||
void start_worker(LocalSchedulerState *state,
|
||||
ActorID actor_id,
|
||||
bool reconstruct);
|
||||
void start_worker(LocalSchedulerState *state);
|
||||
|
||||
/**
|
||||
* Check if a certain quantity of dynamic resources are available. If num_cpus
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "state/task_table.h"
|
||||
#include "state/actor_notification_table.h"
|
||||
#include "state/db_client_table.h"
|
||||
#include "state/local_scheduler_table.h"
|
||||
#include "state/object_table.h"
|
||||
#include "local_scheduler_shared.h"
|
||||
@@ -205,6 +206,8 @@ void provide_scheduler_info(LocalSchedulerState *state,
|
||||
*
|
||||
* @param algorithm_state The state of the scheduling algorithm.
|
||||
* @param actor_id The actor ID of the actor being created.
|
||||
* @param initial_execution_dependency The dummy object ID of the actor
|
||||
* creation task.
|
||||
* @param worker The worker struct for the worker that is running this actor.
|
||||
* If the worker struct has not been created yet (meaning that the worker
|
||||
* that is running this actor has not registered with the local scheduler
|
||||
@@ -213,14 +216,15 @@ void provide_scheduler_info(LocalSchedulerState *state,
|
||||
* @return Void.
|
||||
*/
|
||||
void create_actor(SchedulingAlgorithmState *algorithm_state,
|
||||
ActorID actor_id,
|
||||
const ActorID &actor_id,
|
||||
const ObjectID &initial_execution_dependency,
|
||||
LocalSchedulerClient *worker) {
|
||||
LocalActorInfo entry;
|
||||
entry.task_counters[ActorHandleID::nil()] = 0;
|
||||
entry.frontier_dependencies[ActorHandleID::nil()] = ObjectID::nil();
|
||||
/* The actor has not yet executed any tasks, so there are no execution
|
||||
* dependencies for the next task to be scheduled. */
|
||||
entry.execution_dependency = ObjectID::nil();
|
||||
entry.execution_dependency = initial_execution_dependency;
|
||||
entry.task_queue = new std::list<TaskExecutionSpec>();
|
||||
entry.worker = worker;
|
||||
entry.worker_available = false;
|
||||
@@ -315,11 +319,7 @@ bool dispatch_actor_task(LocalSchedulerState *state,
|
||||
* deterministic reconstruction ordering for tasks whose updates are
|
||||
* reflected in the task table. */
|
||||
std::vector<ObjectID> ordered_execution_dependencies;
|
||||
/* Only overwrite execution dependencies for tasks that have a
|
||||
* submission-time dependency (meaning it is not the initial task). */
|
||||
if (!entry.execution_dependency.is_nil()) {
|
||||
ordered_execution_dependencies.push_back(entry.execution_dependency);
|
||||
}
|
||||
ordered_execution_dependencies.push_back(entry.execution_dependency);
|
||||
task->SetExecutionDependencies(ordered_execution_dependencies);
|
||||
|
||||
/* Assign the first task in the task queue to the worker and mark the worker
|
||||
@@ -342,19 +342,21 @@ bool dispatch_actor_task(LocalSchedulerState *state,
|
||||
return true;
|
||||
}
|
||||
|
||||
void handle_actor_worker_connect(LocalSchedulerState *state,
|
||||
SchedulingAlgorithmState *algorithm_state,
|
||||
ActorID actor_id,
|
||||
LocalSchedulerClient *worker) {
|
||||
void handle_convert_worker_to_actor(
|
||||
LocalSchedulerState *state,
|
||||
SchedulingAlgorithmState *algorithm_state,
|
||||
const ActorID &actor_id,
|
||||
const ObjectID &initial_execution_dependency,
|
||||
LocalSchedulerClient *worker) {
|
||||
if (algorithm_state->local_actor_infos.count(actor_id) == 0) {
|
||||
create_actor(algorithm_state, actor_id, worker);
|
||||
create_actor(algorithm_state, actor_id, initial_execution_dependency,
|
||||
worker);
|
||||
} else {
|
||||
/* In this case, the LocalActorInfo struct was already been created by the
|
||||
* first call to add_task_to_actor_queue. However, the worker field was not
|
||||
* filled out, so fill out the correct worker field now. */
|
||||
algorithm_state->local_actor_infos[actor_id].worker = worker;
|
||||
}
|
||||
dispatch_actor_task(state, algorithm_state, actor_id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -420,14 +422,6 @@ void insert_actor_task_queue(LocalSchedulerState *state,
|
||||
return;
|
||||
}
|
||||
|
||||
/* Handle the case in which there is no LocalActorInfo struct yet. */
|
||||
if (algorithm_state->local_actor_infos.count(actor_id) == 0) {
|
||||
/* Create the actor struct with a NULL worker because the worker struct has
|
||||
* not been created yet. The correct worker struct will be inserted when the
|
||||
* actor worker connects to the local scheduler. */
|
||||
create_actor(algorithm_state, actor_id, NULL);
|
||||
RAY_CHECK(algorithm_state->local_actor_infos.count(actor_id) == 1);
|
||||
}
|
||||
LocalActorInfo &entry =
|
||||
algorithm_state->local_actor_infos.find(actor_id)->second;
|
||||
if (entry.task_counters.count(task_handle_id) == 0) {
|
||||
@@ -799,6 +793,40 @@ int reconstruct_object_timeout_handler(event_loop *loop,
|
||||
.local_scheduler_reconstruction_timeout_milliseconds();
|
||||
}
|
||||
|
||||
int rerun_actor_creation_tasks_timeout_handler(event_loop *loop,
|
||||
timer_id id,
|
||||
void *context) {
|
||||
int64_t start_time = current_time_ms();
|
||||
|
||||
LocalSchedulerState *state = (LocalSchedulerState *) context;
|
||||
|
||||
// Create a set of the dummy object IDs for the actor creation tasks to
|
||||
// reconstruct.
|
||||
std::unordered_set<ObjectID, UniqueIDHasher> actor_dummy_objects;
|
||||
for (auto const &execution_spec :
|
||||
state->algorithm_state->cached_submitted_actor_tasks) {
|
||||
ObjectID actor_creation_dummy_object_id =
|
||||
TaskSpec_actor_creation_dummy_object_id(execution_spec.Spec());
|
||||
actor_dummy_objects.insert(actor_creation_dummy_object_id);
|
||||
}
|
||||
|
||||
// Issue reconstruct calls.
|
||||
for (auto const &object_id : actor_dummy_objects) {
|
||||
reconstruct_object(state, object_id);
|
||||
}
|
||||
|
||||
// Print a warning if this method took too long.
|
||||
int64_t end_time = current_time_ms();
|
||||
if (end_time - start_time >
|
||||
RayConfig::instance().max_time_for_handler_milliseconds()) {
|
||||
RAY_LOG(WARNING) << "reconstruct_object_timeout_handler took "
|
||||
<< end_time - start_time << " milliseconds.";
|
||||
}
|
||||
|
||||
return RayConfig::instance()
|
||||
.local_scheduler_reconstruction_timeout_milliseconds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are still some resources available and false otherwise.
|
||||
*
|
||||
@@ -855,7 +883,7 @@ void dispatch_tasks(LocalSchedulerState *state,
|
||||
if (state->child_pids.size() == 0) {
|
||||
/* If there are no workers, including those pending PID registration,
|
||||
* then we must start a new one to replenish the worker pool. */
|
||||
start_worker(state, ActorID::nil(), false);
|
||||
start_worker(state);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -904,10 +932,9 @@ void dispatch_all_tasks(LocalSchedulerState *state,
|
||||
/* Attempt to dispatch actor tasks. */
|
||||
auto it = algorithm_state->actors_with_pending_tasks.begin();
|
||||
while (it != algorithm_state->actors_with_pending_tasks.end()) {
|
||||
/* Terminate early if there are no more resources available. */
|
||||
if (!resources_available(state)) {
|
||||
break;
|
||||
}
|
||||
// We cannot short-circuit and exit here if there are no resources
|
||||
// available because actor methods may require 0 CPUs.
|
||||
|
||||
/* We increment the iterator ahead of time because the call to
|
||||
* dispatch_actor_task may invalidate the current iterator. */
|
||||
ActorID actor_id = *it;
|
||||
@@ -1078,18 +1105,46 @@ void give_task_to_local_scheduler_retry(UniqueID id,
|
||||
RAY_CHECK(TaskSpec_is_actor_task(spec));
|
||||
|
||||
ActorID actor_id = TaskSpec_actor_id(spec);
|
||||
RAY_CHECK(state->actor_mapping.count(actor_id) == 1);
|
||||
|
||||
if (state->actor_mapping[actor_id].local_scheduler_id ==
|
||||
get_db_client_id(state->db)) {
|
||||
/* The task is now scheduled to us. Call the callback directly. */
|
||||
handle_task_scheduled(state, state->algorithm_state, *execution_spec);
|
||||
} else {
|
||||
/* The task is scheduled to a remote local scheduler. Try to hand it to
|
||||
* them again. */
|
||||
if (state->actor_mapping.count(actor_id) == 0) {
|
||||
// Process the actor task submission again. This will cache the task
|
||||
// locally until a new actor creation notification is broadcast. We will
|
||||
// attempt to reissue the actor creation tasks for all cached actor tasks
|
||||
// in rerun_actor_creation_tasks_timeout_handler.
|
||||
handle_actor_task_submitted(state, state->algorithm_state, *execution_spec);
|
||||
return;
|
||||
}
|
||||
|
||||
DBClientID remote_local_scheduler_id =
|
||||
state->actor_mapping[actor_id].local_scheduler_id;
|
||||
|
||||
// TODO(rkn): db_client_table_cache_get is a blocking call, is this a
|
||||
// performance issue?
|
||||
DBClient remote_local_scheduler =
|
||||
db_client_table_cache_get(state->db, remote_local_scheduler_id);
|
||||
|
||||
// Check if the local scheduler that we're assigning this task to is still
|
||||
// alive.
|
||||
if (remote_local_scheduler.is_alive) {
|
||||
// The local scheduler is still alive, which means that perhaps it hasn't
|
||||
// subscribed to the appropriate channel yet, so retrying should suffice.
|
||||
// This should be rare.
|
||||
give_task_to_local_scheduler(
|
||||
state, state->algorithm_state, *execution_spec,
|
||||
state->actor_mapping[actor_id].local_scheduler_id);
|
||||
} else {
|
||||
// The local scheduler is dead, so we will need to recreate the actor by
|
||||
// invoking reconstruction.
|
||||
RAY_LOG(INFO) << "Local scheduler " << remote_local_scheduler_id
|
||||
<< " that was running actor " << actor_id << " died.";
|
||||
RAY_CHECK(state->actor_mapping.count(actor_id) == 1);
|
||||
// Update the actor mapping.
|
||||
state->actor_mapping.erase(actor_id);
|
||||
// Process the actor task submission again. This will cache the task
|
||||
// locally until a new actor creation notification is broadcast. We will
|
||||
// attempt to reissue the actor creation tasks for all cached actor tasks
|
||||
// in rerun_actor_creation_tasks_timeout_handler.
|
||||
handle_actor_task_submitted(state, state->algorithm_state, *execution_spec);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1188,6 +1243,12 @@ bool resource_constraints_satisfied(LocalSchedulerState *state,
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (TaskSpec_is_actor_creation_task(spec) &&
|
||||
state->static_resources["CPU"] != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1199,10 +1260,10 @@ void handle_task_submitted(LocalSchedulerState *state,
|
||||
* resource is currently unavailable, then consider queueing task locally and
|
||||
* recheck dynamic next time. */
|
||||
|
||||
/* If this task's constraints are satisfied, dependencies are available
|
||||
* locally, and there is an available worker, then enqueue the task in the
|
||||
* dispatch queue and trigger task dispatch. Otherwise, pass the task along to
|
||||
* the global scheduler if there is one. */
|
||||
// If this task's constraints are satisfied, dependencies are available
|
||||
// locally, and there is an available worker, then enqueue the task in the
|
||||
// dispatch queue and trigger task dispatch. Otherwise, pass the task along to
|
||||
// the global scheduler if there is one.
|
||||
if (resource_constraints_satisfied(state, spec) &&
|
||||
(algorithm_state->available_workers.size() > 0) &&
|
||||
can_run(algorithm_state, execution_spec)) {
|
||||
@@ -1224,6 +1285,11 @@ void handle_actor_task_submitted(LocalSchedulerState *state,
|
||||
ActorID actor_id = TaskSpec_actor_id(task_spec);
|
||||
|
||||
if (state->actor_mapping.count(actor_id) == 0) {
|
||||
// Create a copy of the task to write to the task table.
|
||||
Task *task = Task_alloc(
|
||||
task_spec, execution_spec.SpecSize(), TASK_STATUS_ACTOR_CACHED,
|
||||
get_db_client_id(state->db), execution_spec.ExecutionDependencies());
|
||||
|
||||
/* Add this task to a queue of tasks that have been submitted but the local
|
||||
* scheduler doesn't know which actor is responsible for them. These tasks
|
||||
* will be resubmitted (internally by the local scheduler) whenever a new
|
||||
@@ -1232,6 +1298,18 @@ void handle_actor_task_submitted(LocalSchedulerState *state,
|
||||
TaskExecutionSpec task_entry = TaskExecutionSpec(&execution_spec);
|
||||
algorithm_state->cached_submitted_actor_tasks.push_back(
|
||||
std::move(task_entry));
|
||||
|
||||
#if !RAY_USE_NEW_GCS
|
||||
// Even if the task can't be assigned to a worker yet, we should still write
|
||||
// it to the task table. TODO(rkn): There's no need to do this more than
|
||||
// once, and we could run into problems if we have very large numbers of
|
||||
// tasks in this cache.
|
||||
task_table_add_task(state->db, task, NULL, NULL, NULL);
|
||||
#else
|
||||
RAY_CHECK_OK(TaskTableAdd(&state->gcs_client, task));
|
||||
Task_free(task);
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1255,8 +1333,7 @@ void handle_actor_task_submitted(LocalSchedulerState *state,
|
||||
void handle_actor_creation_notification(
|
||||
LocalSchedulerState *state,
|
||||
SchedulingAlgorithmState *algorithm_state,
|
||||
ActorID actor_id,
|
||||
bool reconstruct) {
|
||||
ActorID actor_id) {
|
||||
int num_cached_actor_tasks =
|
||||
algorithm_state->cached_submitted_actor_tasks.size();
|
||||
|
||||
@@ -1281,7 +1358,12 @@ void handle_task_scheduled(LocalSchedulerState *state,
|
||||
* the database. */
|
||||
RAY_CHECK(state->db != NULL);
|
||||
RAY_CHECK(state->config.global_scheduler_exists);
|
||||
/* Push the task to the appropriate queue. */
|
||||
|
||||
// Currently, the global scheduler will never assign a task to a local
|
||||
// scheduler that has 0 CPUs.
|
||||
RAY_CHECK(state->static_resources["CPU"] != 0);
|
||||
|
||||
// Push the task to the appropriate queue.
|
||||
queue_task_locally(state, algorithm_state, execution_spec, true);
|
||||
dispatch_tasks(state, algorithm_state);
|
||||
}
|
||||
@@ -1652,6 +1734,18 @@ void handle_driver_removed(LocalSchedulerState *state,
|
||||
}
|
||||
}
|
||||
|
||||
// Remove this driver's tasks from the cached actor tasks. Note that this loop
|
||||
// could be very slow if the vector of cached actor tasks is very long.
|
||||
for (auto it = algorithm_state->cached_submitted_actor_tasks.begin();
|
||||
it != algorithm_state->cached_submitted_actor_tasks.end();) {
|
||||
TaskSpec *spec = (*it).Spec();
|
||||
if (TaskSpec_driver_id(spec) == driver_id) {
|
||||
it = algorithm_state->cached_submitted_actor_tasks.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO(rkn): Should we clean up the actor data structures? */
|
||||
}
|
||||
|
||||
|
||||
@@ -76,14 +76,12 @@ void handle_actor_task_submitted(LocalSchedulerState *state,
|
||||
* @param state The state of the local scheduler.
|
||||
* @param algorithm_state State maintained by the scheduling algorithm.
|
||||
* @param actor_id The ID of the actor being created.
|
||||
* @param reconstruct True if the actor is being created in "reconstruct" mode.
|
||||
* @return Void.
|
||||
*/
|
||||
void handle_actor_creation_notification(
|
||||
LocalSchedulerState *state,
|
||||
SchedulingAlgorithmState *algorithm_state,
|
||||
ActorID actor_id,
|
||||
bool reconstruct);
|
||||
ActorID actor_id);
|
||||
|
||||
/**
|
||||
* This function will be called when a task is assigned by the global scheduler
|
||||
@@ -177,13 +175,17 @@ void handle_actor_worker_available(LocalSchedulerState *state,
|
||||
* @param state The state of the local scheduler.
|
||||
* @param algorithm_state State maintained by the scheduling algorithm.
|
||||
* @param actor_id The ID of the actor running on the worker.
|
||||
* @param worker The worker that was connected.
|
||||
* @param initial_execution_dependency The dummy object ID of the actor
|
||||
* creation task.
|
||||
* @param worker The worker that was converted to an actor.
|
||||
* @return Void.
|
||||
*/
|
||||
void handle_actor_worker_connect(LocalSchedulerState *state,
|
||||
SchedulingAlgorithmState *algorithm_state,
|
||||
ActorID actor_id,
|
||||
LocalSchedulerClient *worker);
|
||||
void handle_convert_worker_to_actor(
|
||||
LocalSchedulerState *state,
|
||||
SchedulingAlgorithmState *algorithm_state,
|
||||
const ActorID &actor_id,
|
||||
const ObjectID &initial_execution_dependency,
|
||||
LocalSchedulerClient *worker);
|
||||
|
||||
/**
|
||||
* Handle the fact that a worker running an actor has disconnected.
|
||||
@@ -292,6 +294,19 @@ int fetch_object_timeout_handler(event_loop *loop, timer_id id, void *context);
|
||||
int reconstruct_object_timeout_handler(event_loop *loop,
|
||||
timer_id id,
|
||||
void *context);
|
||||
|
||||
/// This function initiates reconstruction for the actor creation tasks
|
||||
/// corresponding to the actor tasks cached in the local scheduler.
|
||||
///
|
||||
/// \param loop The local scheduler's event loop.
|
||||
/// \param id The ID of the timer that triggers this function.
|
||||
/// \param context The function's context.
|
||||
/// \return An integer representing the time interval in seconds before the
|
||||
/// next invocation of the function.
|
||||
int rerun_actor_creation_tasks_timeout_handler(event_loop *loop,
|
||||
timer_id id,
|
||||
void *context);
|
||||
|
||||
/**
|
||||
* Check whether an object, including actor dummy objects, is locally
|
||||
* available.
|
||||
|
||||
@@ -12,49 +12,22 @@
|
||||
LocalSchedulerConnection *LocalSchedulerConnection_init(
|
||||
const char *local_scheduler_socket,
|
||||
UniqueID client_id,
|
||||
ActorID actor_id,
|
||||
bool is_worker,
|
||||
int64_t num_gpus) {
|
||||
bool is_worker) {
|
||||
LocalSchedulerConnection *result = new LocalSchedulerConnection();
|
||||
result->conn = connect_ipc_sock_retry(local_scheduler_socket, -1, -1);
|
||||
result->actor_id = actor_id;
|
||||
|
||||
/* Register with the local scheduler.
|
||||
* NOTE(swang): If the local scheduler exits and we are registered as a
|
||||
* worker, we will get killed. */
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
auto message = CreateRegisterClientRequest(
|
||||
fbb, is_worker, to_flatbuf(fbb, client_id),
|
||||
to_flatbuf(fbb, result->actor_id), getpid(), num_gpus);
|
||||
fbb, is_worker, to_flatbuf(fbb, client_id), getpid());
|
||||
fbb.Finish(message);
|
||||
/* Register the process ID with the local scheduler. */
|
||||
int success = write_message(result->conn, MessageType_RegisterClientRequest,
|
||||
fbb.GetSize(), fbb.GetBufferPointer());
|
||||
RAY_CHECK(success == 0) << "Unable to register worker with local scheduler";
|
||||
|
||||
/* Wait for a confirmation from the local scheduler. */
|
||||
int64_t type;
|
||||
int64_t reply_size;
|
||||
uint8_t *reply;
|
||||
read_message(result->conn, &type, &reply_size, &reply);
|
||||
if (type == DISCONNECT_CLIENT) {
|
||||
RAY_LOG(DEBUG) << "Exiting because local scheduler closed connection.";
|
||||
exit(1);
|
||||
}
|
||||
RAY_CHECK(type == MessageType_RegisterClientReply);
|
||||
|
||||
/* Parse the reply object. */
|
||||
auto reply_message = flatbuffers::GetRoot<RegisterClientReply>(reply);
|
||||
for (size_t i = 0; i < reply_message->gpu_ids()->size(); ++i) {
|
||||
result->gpu_ids.push_back(reply_message->gpu_ids()->Get(i));
|
||||
}
|
||||
/* If the worker is not an actor, there should not be any GPU IDs here. */
|
||||
if (ActorID_equal(result->actor_id, ActorID::nil())) {
|
||||
RAY_CHECK(reply_message->gpu_ids()->size() == 0);
|
||||
}
|
||||
|
||||
free(reply);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -119,20 +92,21 @@ TaskSpec *local_scheduler_get_task(LocalSchedulerConnection *conn,
|
||||
/* Parse the flatbuffer object. */
|
||||
auto reply_message = flatbuffers::GetRoot<GetTaskReply>(reply);
|
||||
|
||||
/* Set the GPU IDs for this task. We only do this for non-actor tasks because
|
||||
* for actors the GPUs are associated with the actor itself and not with the
|
||||
* actor methods. */
|
||||
if (ActorID_equal(conn->actor_id, ActorID::nil())) {
|
||||
/* Create a copy of the task spec so we can free the reply. */
|
||||
*task_size = reply_message->task_spec()->size();
|
||||
TaskSpec *data = (TaskSpec *) reply_message->task_spec()->data();
|
||||
TaskSpec *spec = TaskSpec_copy(data, *task_size);
|
||||
|
||||
// Set the GPU IDs for this task. We only do this for non-actor tasks because
|
||||
// for actors the GPUs are associated with the actor itself and not with the
|
||||
// actor methods. Note that this also processes GPUs for actor creation tasks.
|
||||
if (!TaskSpec_is_actor_task(spec)) {
|
||||
conn->gpu_ids.clear();
|
||||
for (size_t i = 0; i < reply_message->gpu_ids()->size(); ++i) {
|
||||
conn->gpu_ids.push_back(reply_message->gpu_ids()->Get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a copy of the task spec so we can free the reply. */
|
||||
*task_size = reply_message->task_spec()->size();
|
||||
TaskSpec *data = (TaskSpec *) reply_message->task_spec()->data();
|
||||
TaskSpec *spec = TaskSpec_copy(data, *task_size);
|
||||
/* Free the original message from the local scheduler. */
|
||||
free(reply);
|
||||
/* Return the copy of the task spec and pass ownership to the caller. */
|
||||
|
||||
@@ -8,9 +8,6 @@ struct LocalSchedulerConnection {
|
||||
/** File descriptor of the Unix domain socket that connects to local
|
||||
* scheduler. */
|
||||
int conn;
|
||||
/** The actor ID of this client. If this client is not an actor, then this
|
||||
* should be NIL_ACTOR_ID. */
|
||||
ActorID actor_id;
|
||||
/** The IDs of the GPUs that this client can use. */
|
||||
std::vector<int> gpu_ids;
|
||||
};
|
||||
@@ -20,20 +17,14 @@ struct LocalSchedulerConnection {
|
||||
*
|
||||
* @param local_scheduler_socket The name of the socket to use to connect to the
|
||||
* local scheduler.
|
||||
* @param actor_id The ID of the actor running on this worker. If no actor is
|
||||
* running on this actor, this should be NIL_ACTOR_ID.
|
||||
* @param is_worker Whether this client is a worker. If it is a worker, an
|
||||
* additional message will be sent to register as one.
|
||||
* @param num_gpus The number of GPUs required by this worker. This is only
|
||||
* used if the worker is an actor.
|
||||
* @return The connection information.
|
||||
*/
|
||||
LocalSchedulerConnection *LocalSchedulerConnection_init(
|
||||
const char *local_scheduler_socket,
|
||||
UniqueID worker_id,
|
||||
ActorID actor_id,
|
||||
bool is_worker,
|
||||
int64_t num_gpus);
|
||||
bool is_worker);
|
||||
|
||||
/**
|
||||
* Disconnect from the local scheduler.
|
||||
|
||||
@@ -19,19 +19,15 @@ static int PyLocalSchedulerClient_init(PyLocalSchedulerClient *self,
|
||||
PyObject *kwds) {
|
||||
char *socket_name;
|
||||
UniqueID client_id;
|
||||
ActorID actor_id;
|
||||
PyObject *is_worker;
|
||||
int num_gpus;
|
||||
if (!PyArg_ParseTuple(args, "sO&O&Oi", &socket_name, PyStringToUniqueID,
|
||||
&client_id, PyStringToUniqueID, &actor_id, &is_worker,
|
||||
&num_gpus)) {
|
||||
if (!PyArg_ParseTuple(args, "sO&O", &socket_name, PyStringToUniqueID,
|
||||
&client_id, &is_worker)) {
|
||||
self->local_scheduler_connection = NULL;
|
||||
return -1;
|
||||
}
|
||||
/* Connect to the local scheduler. */
|
||||
self->local_scheduler_connection = LocalSchedulerConnection_init(
|
||||
socket_name, client_id, actor_id, (bool) PyObject_IsTrue(is_worker),
|
||||
num_gpus);
|
||||
socket_name, client_id, (bool) PyObject_IsTrue(is_worker));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -124,9 +124,8 @@ LocalSchedulerMock *LocalSchedulerMock_init(int num_workers,
|
||||
std::thread(register_clients, num_mock_workers, mock);
|
||||
|
||||
for (int i = 0; i < num_mock_workers; ++i) {
|
||||
mock->conns[i] =
|
||||
LocalSchedulerConnection_init(local_scheduler_socket_name.c_str(),
|
||||
WorkerID::nil(), ActorID::nil(), true, 0);
|
||||
mock->conns[i] = LocalSchedulerConnection_init(
|
||||
local_scheduler_socket_name.c_str(), WorkerID::nil(), true);
|
||||
}
|
||||
|
||||
background_thread.join();
|
||||
@@ -666,7 +665,7 @@ TEST start_kill_workers_test(void) {
|
||||
static_cast<size_t>(num_workers - 1));
|
||||
|
||||
/* Start a worker after the local scheduler has been initialized. */
|
||||
start_worker(local_scheduler->local_scheduler_state, ActorID::nil(), false);
|
||||
start_worker(local_scheduler->local_scheduler_state);
|
||||
/* Accept the workers as clients to the plasma manager. */
|
||||
int new_worker_fd = accept_client(local_scheduler->plasma_manager_fd);
|
||||
/* The new worker should register its process ID. */
|
||||
|
||||
@@ -104,7 +104,8 @@ TaskSpecification::TaskSpecification(
|
||||
// Serialize the TaskSpecification.
|
||||
auto spec = CreateTaskInfo(
|
||||
fbb, to_flatbuf(fbb, driver_id), to_flatbuf(fbb, task_id),
|
||||
to_flatbuf(fbb, parent_task_id), parent_counter, to_flatbuf(fbb, WorkerID::nil()),
|
||||
to_flatbuf(fbb, parent_task_id), parent_counter, to_flatbuf(fbb, ActorID::nil()),
|
||||
to_flatbuf(fbb, ActorID::nil()), to_flatbuf(fbb, WorkerID::nil()),
|
||||
to_flatbuf(fbb, ActorHandleID::nil()), 0, false, to_flatbuf(fbb, function_id),
|
||||
fbb.CreateVector(arguments), fbb.CreateVector(returns),
|
||||
map_to_flatbuf(fbb, required_resources));
|
||||
|
||||
Reference in New Issue
Block a user