mirror of
https://github.com/wassname/ray.git
synced 2026-08-05 13:21:03 +08:00
Push an error to the driver when the workload hangs on ray.put reconstruction (#382)
* Fix worker blocked bug * tmp * Push an error to the driver on ray.put for non-driver tasks * Fix result table tests * Fix test, logging * Address comments * Fix suppression bug * Fix redis module test * Edit error message * Get values in chunks during reconstruction * Test case for driver ray.put errors * Error for evicting ray.put objects from the driver * Fix tests * Reduce verbosity * Documentation
This commit is contained in:
committed by
Robert Nishihara
parent
4618fd45b1
commit
083e7a28ad
@@ -90,6 +90,9 @@ table TaskReply {
|
||||
local_scheduler_id: string;
|
||||
// A string of bytes representing the task specification.
|
||||
task_spec: string;
|
||||
// A boolean representing whether the update was successful. This field
|
||||
// should only be used for test-and-set operations.
|
||||
updated: bool;
|
||||
}
|
||||
|
||||
root_type TaskReply;
|
||||
@@ -127,3 +130,12 @@ table LocalSchedulerInfoMessage {
|
||||
}
|
||||
|
||||
root_type LocalSchedulerInfoMessage;
|
||||
|
||||
table ResultTableReply {
|
||||
// The task ID of the task that created the object.
|
||||
task_id: string;
|
||||
// Whether the task created the object through a ray.put.
|
||||
is_put: bool;
|
||||
}
|
||||
|
||||
root_type ResultTableReply;
|
||||
|
||||
+9
-2
@@ -116,6 +116,10 @@ int connect_ipc_sock_retry(const char *socket_pathname,
|
||||
if (fd >= 0) {
|
||||
break;
|
||||
}
|
||||
if (num_attempts == 0) {
|
||||
LOG_ERROR("Connection to socket failed for pathname %s.",
|
||||
socket_pathname);
|
||||
}
|
||||
/* Sleep for timeout milliseconds. */
|
||||
usleep(timeout * 1000);
|
||||
}
|
||||
@@ -147,7 +151,7 @@ int connect_ipc_sock(const char *socket_pathname) {
|
||||
|
||||
if (connect(socket_fd, (struct sockaddr *) &socket_address,
|
||||
sizeof(socket_address)) != 0) {
|
||||
LOG_ERROR("Connection to socket failed for pathname %s.", socket_pathname);
|
||||
close(socket_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -173,6 +177,10 @@ int connect_inet_sock_retry(const char *ip_addr,
|
||||
if (fd >= 0) {
|
||||
break;
|
||||
}
|
||||
if (num_attempts == 0) {
|
||||
LOG_ERROR("Connection to socket failed for address %s:%d.", ip_addr,
|
||||
port);
|
||||
}
|
||||
/* Sleep for timeout milliseconds. */
|
||||
usleep(timeout * 1000);
|
||||
}
|
||||
@@ -203,7 +211,6 @@ int connect_inet_sock(const char *ip_addr, int port) {
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0) {
|
||||
LOG_ERROR("Connection to socket failed for address %s:%d.", ip_addr, port);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -543,14 +543,3 @@ PyObject *check_simple_value(PyObject *self, PyObject *args) {
|
||||
}
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
PyObject *compute_put_id(PyObject *self, PyObject *args) {
|
||||
int put_index;
|
||||
TaskID task_id;
|
||||
if (!PyArg_ParseTuple(args, "O&i", &PyObjectToUniqueID, &task_id,
|
||||
&put_index)) {
|
||||
return NULL;
|
||||
}
|
||||
ObjectID put_id = task_compute_put_id(task_id, put_index);
|
||||
return PyObjectID_make(put_id);
|
||||
}
|
||||
|
||||
@@ -690,27 +690,40 @@ int ObjectInfoSubscribe_RedisCommand(RedisModuleCtx *ctx,
|
||||
*
|
||||
* This is called from a client with the command:
|
||||
*
|
||||
* RAY.RESULT_TABLE_ADD <object id> <task id>
|
||||
* RAY.RESULT_TABLE_ADD <object id> <task id> <is_put>
|
||||
*
|
||||
* @param object_id A string representing the object ID.
|
||||
* @param task_id A string representing the task ID of the task that produced
|
||||
* the object.
|
||||
* @param is_put An integer that is 1 if the object was created through ray.put
|
||||
* and 0 if created by return value.
|
||||
* @return OK if the operation was successful.
|
||||
*/
|
||||
int ResultTableAdd_RedisCommand(RedisModuleCtx *ctx,
|
||||
RedisModuleString **argv,
|
||||
int argc) {
|
||||
if (argc != 3) {
|
||||
if (argc != 4) {
|
||||
return RedisModule_WrongArity(ctx);
|
||||
}
|
||||
|
||||
/* Set the task ID under field "task" in the object info table. */
|
||||
RedisModuleString *object_id = argv[1];
|
||||
RedisModuleString *task_id = argv[2];
|
||||
RedisModuleString *is_put = argv[3];
|
||||
|
||||
/* Check to make sure the is_put field was a 0 or a 1. */
|
||||
long long is_put_integer;
|
||||
if ((RedisModule_StringToLongLong(is_put, &is_put_integer) !=
|
||||
REDISMODULE_OK) ||
|
||||
(is_put_integer != 0 && is_put_integer != 1)) {
|
||||
return RedisModule_ReplyWithError(
|
||||
ctx, "The is_put field must be either a 0 or a 1.");
|
||||
}
|
||||
|
||||
RedisModuleKey *key;
|
||||
key = OpenPrefixedKey(ctx, OBJECT_INFO_PREFIX, object_id, REDISMODULE_WRITE);
|
||||
RedisModule_HashSet(key, REDISMODULE_HASH_CFIELDS, "task", task_id, NULL);
|
||||
RedisModule_HashSet(key, REDISMODULE_HASH_CFIELDS, "task", task_id, "is_put",
|
||||
is_put, NULL);
|
||||
|
||||
/* Clean up. */
|
||||
RedisModule_CloseKey(key);
|
||||
@@ -723,13 +736,19 @@ int ResultTableAdd_RedisCommand(RedisModuleCtx *ctx,
|
||||
* Reply with information about a task ID. This is used by
|
||||
* RAY.RESULT_TABLE_LOOKUP and RAY.TASK_TABLE_GET.
|
||||
*
|
||||
* @param ctx The Redis context.
|
||||
* @param task_id The task ID of the task to reply about.
|
||||
* @param updated A boolean representing whether the task was updated during
|
||||
* this operation. This field is only used for
|
||||
* RAY.TASK_TABLE_TEST_AND_UPDATE operations.
|
||||
* @return NIL if the task ID is not in the task table. An error if the task ID
|
||||
* is in the task table but the appropriate fields are not there, and
|
||||
* an array of the task scheduling state, the local scheduler ID, and
|
||||
* the task spec for the task otherwise.
|
||||
*/
|
||||
int ReplyWithTask(RedisModuleCtx *ctx, RedisModuleString *task_id) {
|
||||
int ReplyWithTask(RedisModuleCtx *ctx,
|
||||
RedisModuleString *task_id,
|
||||
bool updated) {
|
||||
RedisModuleKey *key =
|
||||
OpenPrefixedKey(ctx, TASK_PREFIX, task_id, REDISMODULE_READ);
|
||||
|
||||
@@ -762,7 +781,7 @@ int ReplyWithTask(RedisModuleCtx *ctx, RedisModuleString *task_id) {
|
||||
auto message =
|
||||
CreateTaskReply(fbb, RedisStringToFlatbuf(fbb, task_id), state_integer,
|
||||
RedisStringToFlatbuf(fbb, local_scheduler_id),
|
||||
RedisStringToFlatbuf(fbb, task_spec));
|
||||
RedisStringToFlatbuf(fbb, task_spec), updated);
|
||||
fbb.Finish(message);
|
||||
|
||||
RedisModuleString *reply = RedisModule_CreateString(
|
||||
@@ -790,10 +809,8 @@ int ReplyWithTask(RedisModuleCtx *ctx, RedisModuleString *task_id) {
|
||||
* RAY.RESULT_TABLE_LOOKUP <object id>
|
||||
*
|
||||
* @param object_id A string representing the object ID.
|
||||
* @return NIL if the object ID is not in the result table or if the
|
||||
* corresponding task ID is not in the task table. Otherwise, this
|
||||
* returns an array of the scheduling state, the local scheduler ID, and
|
||||
* the task spec for the task corresponding to this object ID.
|
||||
* @return NIL if the object ID is not in the result table. Otherwise, this
|
||||
* returns a ResultTableReply flatbuffer.
|
||||
*/
|
||||
int ResultTableLookup_RedisCommand(RedisModuleCtx *ctx,
|
||||
RedisModuleString **argv,
|
||||
@@ -814,13 +831,36 @@ int ResultTableLookup_RedisCommand(RedisModuleCtx *ctx,
|
||||
}
|
||||
|
||||
RedisModuleString *task_id;
|
||||
RedisModule_HashGet(key, REDISMODULE_HASH_CFIELDS, "task", &task_id, NULL);
|
||||
RedisModuleString *is_put;
|
||||
RedisModule_HashGet(key, REDISMODULE_HASH_CFIELDS, "task", &task_id, "is_put",
|
||||
&is_put, NULL);
|
||||
RedisModule_CloseKey(key);
|
||||
if (task_id == NULL) {
|
||||
if (task_id == NULL || is_put == NULL) {
|
||||
return RedisModule_ReplyWithNull(ctx);
|
||||
}
|
||||
|
||||
RedisModule_ReplyWithString(ctx, task_id);
|
||||
/* Check to make sure the is_put field was a 0 or a 1. */
|
||||
long long is_put_integer;
|
||||
if (RedisModule_StringToLongLong(is_put, &is_put_integer) != REDISMODULE_OK ||
|
||||
(is_put_integer != 0 && is_put_integer != 1)) {
|
||||
RedisModule_FreeString(ctx, is_put);
|
||||
RedisModule_FreeString(ctx, task_id);
|
||||
return RedisModule_ReplyWithError(
|
||||
ctx, "The is_put field must be either a 0 or a 1.");
|
||||
}
|
||||
|
||||
/* Make and return the flatbuffer reply. */
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
auto message = CreateResultTableReply(fbb, RedisStringToFlatbuf(fbb, task_id),
|
||||
bool(is_put_integer));
|
||||
fbb.Finish(message);
|
||||
RedisModuleString *reply = RedisModule_CreateString(
|
||||
ctx, (const char *) fbb.GetBufferPointer(), fbb.GetSize());
|
||||
RedisModule_ReplyWithString(ctx, reply);
|
||||
|
||||
/* Clean up. */
|
||||
RedisModule_FreeString(ctx, reply);
|
||||
RedisModule_FreeString(ctx, is_put);
|
||||
RedisModule_FreeString(ctx, task_id);
|
||||
|
||||
return REDISMODULE_OK;
|
||||
@@ -971,12 +1011,8 @@ int TaskTableUpdate_RedisCommand(RedisModuleCtx *ctx,
|
||||
* instance) to update the task entry with.
|
||||
* @param ray_client_id A string that is the ray client ID of the associated
|
||||
* local scheduler, if any, to update the task entry with.
|
||||
* @return If the current scheduling state does not match the test bitmask,
|
||||
* returns nil. Else, returns the same as RAY.TASK_TABLE_GET: an array
|
||||
* of strings representing the updated task fields in the following
|
||||
* order: 1) (integer) scheduling state 2) (string) associated local
|
||||
* scheduler ID, if any 3) (string) the task specification, which can be
|
||||
* cast to a task_spec.
|
||||
* @return Returns the task entry as a TaskReply. The reply will reflect the
|
||||
* update, if it happened.
|
||||
*/
|
||||
int TaskTableTestAndUpdate_RedisCommand(RedisModuleCtx *ctx,
|
||||
RedisModuleString **argv,
|
||||
@@ -1015,20 +1051,19 @@ int TaskTableTestAndUpdate_RedisCommand(RedisModuleCtx *ctx,
|
||||
return RedisModule_ReplyWithError(
|
||||
ctx, "Invalid test value for scheduling state");
|
||||
}
|
||||
if ((current_state_integer & test_state_bitmask) == 0) {
|
||||
/* The current value does not match the test bitmask, so do not perform the
|
||||
* update. */
|
||||
RedisModule_CloseKey(key);
|
||||
return RedisModule_ReplyWithNull(ctx);
|
||||
|
||||
bool updated = false;
|
||||
if (current_state_integer & test_state_bitmask) {
|
||||
/* The test passed, so perform the update. */
|
||||
RedisModule_HashSet(key, REDISMODULE_HASH_CFIELDS, "state", state,
|
||||
"local_scheduler_id", argv[4], NULL);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
/* The test passed, so perform the update. */
|
||||
RedisModule_HashSet(key, REDISMODULE_HASH_CFIELDS, "state", state,
|
||||
"local_scheduler_id", argv[4], NULL);
|
||||
/* Clean up. */
|
||||
RedisModule_CloseKey(key);
|
||||
/* Construct a reply by getting the task from the task ID. */
|
||||
return ReplyWithTask(ctx, argv[1]);
|
||||
return ReplyWithTask(ctx, argv[1], updated);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1052,7 +1087,7 @@ int TaskTableGet_RedisCommand(RedisModuleCtx *ctx,
|
||||
}
|
||||
|
||||
/* Construct a reply by getting the task from the task ID. */
|
||||
return ReplyWithTask(ctx, argv[1]);
|
||||
return ReplyWithTask(ctx, argv[1], false);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
@@ -18,14 +18,19 @@ typedef enum {
|
||||
/** An object was added with a different hash from the existing
|
||||
* one. */
|
||||
OBJECT_HASH_MISMATCH_ERROR_INDEX = 0,
|
||||
/** An object that was created through a ray.put is lost. */
|
||||
PUT_RECONSTRUCTION_ERROR_INDEX,
|
||||
/** The total number of error types. */
|
||||
MAX_ERROR_INDEX
|
||||
} error_index;
|
||||
|
||||
/** Information about the error to be displayed to the user. */
|
||||
static const char *error_types[] = {"object_hash_mismatch"};
|
||||
static const char *error_types[] = {"object_hash_mismatch",
|
||||
"put_reconstruction"};
|
||||
static const char *error_messages[] = {
|
||||
"A nondeterministic task was reexecuted."};
|
||||
"A nondeterministic task was reexecuted.",
|
||||
"An object created by ray.put was evicted and could not be reconstructed. "
|
||||
"The driver may need to be restarted."};
|
||||
|
||||
/**
|
||||
* Push an error to the given Python driver.
|
||||
|
||||
@@ -104,13 +104,16 @@ void object_info_subscribe(DBHandle *db_handle,
|
||||
|
||||
void result_table_add(DBHandle *db_handle,
|
||||
ObjectID object_id,
|
||||
TaskID task_id_arg,
|
||||
TaskID task_id,
|
||||
bool is_put,
|
||||
RetryInfo *retry,
|
||||
result_table_done_callback done_callback,
|
||||
void *user_context) {
|
||||
TaskID *task_id_copy = (TaskID *) malloc(sizeof(TaskID));
|
||||
memcpy(task_id_copy, task_id_arg.id, sizeof(*task_id_copy));
|
||||
init_table_callback(db_handle, object_id, __func__, task_id_copy, retry,
|
||||
ResultTableAddInfo *info =
|
||||
(ResultTableAddInfo *) malloc(sizeof(ResultTableAddInfo));
|
||||
info->task_id = task_id;
|
||||
info->is_put = is_put;
|
||||
init_table_callback(db_handle, object_id, __func__, info, retry,
|
||||
(table_done_callback) done_callback,
|
||||
redis_result_table_add, user_context);
|
||||
}
|
||||
|
||||
@@ -224,6 +224,15 @@ typedef struct {
|
||||
typedef void (*result_table_done_callback)(ObjectID object_id,
|
||||
void *user_context);
|
||||
|
||||
/** Information about a result table entry to add. */
|
||||
typedef struct {
|
||||
/** The task ID of the task that created the requested object. */
|
||||
TaskID task_id;
|
||||
/** True if the object was created through a put, and false if created by
|
||||
* return value. */
|
||||
bool is_put;
|
||||
} ResultTableAddInfo;
|
||||
|
||||
/**
|
||||
* Add information about a new object to the object table. This
|
||||
* is immutable information like the ID of the task that
|
||||
@@ -232,6 +241,8 @@ typedef void (*result_table_done_callback)(ObjectID object_id,
|
||||
* @param db_handle Handle to object_table database.
|
||||
* @param object_id ID of the object to add.
|
||||
* @param task_id ID of the task that creates this object.
|
||||
* @param is_put A boolean that is true if the object was created through a
|
||||
* ray.put, and false if the object was created by return value.
|
||||
* @param retry Information about retrying the request to the database.
|
||||
* @param done_callback Function to be called when database returns result.
|
||||
* @param user_context Context passed by the caller.
|
||||
@@ -240,6 +251,7 @@ typedef void (*result_table_done_callback)(ObjectID object_id,
|
||||
void result_table_add(DBHandle *db_handle,
|
||||
ObjectID object_id,
|
||||
TaskID task_id,
|
||||
bool is_put,
|
||||
RetryInfo *retry,
|
||||
result_table_done_callback done_callback,
|
||||
void *user_context);
|
||||
@@ -247,6 +259,7 @@ void result_table_add(DBHandle *db_handle,
|
||||
/** Callback called when the result table lookup completes. */
|
||||
typedef void (*result_table_lookup_callback)(ObjectID object_id,
|
||||
TaskID task_id,
|
||||
bool is_put,
|
||||
void *user_context);
|
||||
|
||||
/**
|
||||
|
||||
@@ -342,12 +342,14 @@ void redis_result_table_add(TableCallbackData *callback_data) {
|
||||
CHECK(callback_data);
|
||||
DBHandle *db = callback_data->db_handle;
|
||||
ObjectID id = callback_data->id;
|
||||
TaskID *result_task_id = (TaskID *) callback_data->data;
|
||||
ResultTableAddInfo *info = (ResultTableAddInfo *) callback_data->data;
|
||||
int is_put = info->is_put ? 1 : 0;
|
||||
|
||||
/* Add the result entry to the result table. */
|
||||
int status = redisAsyncCommand(
|
||||
db->context, redis_result_table_add_callback,
|
||||
(void *) callback_data->timer_id, "RAY.RESULT_TABLE_ADD %b %b", id.id,
|
||||
sizeof(id.id), result_task_id->id, sizeof(result_task_id->id));
|
||||
(void *) callback_data->timer_id, "RAY.RESULT_TABLE_ADD %b %b %d", id.id,
|
||||
sizeof(id.id), info->task_id.id, sizeof(info->task_id.id), is_put);
|
||||
if ((status == REDIS_ERR) || db->context->err) {
|
||||
LOG_REDIS_DEBUG(db->context, "Error in result table add");
|
||||
}
|
||||
@@ -386,16 +388,19 @@ void redis_result_table_lookup_callback(redisAsyncContext *c,
|
||||
reply->type);
|
||||
/* Parse the task from the reply. */
|
||||
TaskID result_id = NIL_TASK_ID;
|
||||
bool is_put = false;
|
||||
if (reply->type == REDIS_REPLY_STRING) {
|
||||
CHECK(reply->len == sizeof(result_id));
|
||||
memcpy(&result_id, reply->str, reply->len);
|
||||
auto message = flatbuffers::GetRoot<ResultTableReply>(reply->str);
|
||||
result_id = from_flatbuf(message->task_id());
|
||||
is_put = message->is_put();
|
||||
}
|
||||
|
||||
/* Call the done callback if there is one. */
|
||||
result_table_lookup_callback done_callback =
|
||||
(result_table_lookup_callback) callback_data->done_callback;
|
||||
if (done_callback != NULL) {
|
||||
done_callback(callback_data->id, result_id, callback_data->user_context);
|
||||
done_callback(callback_data->id, result_id, is_put,
|
||||
callback_data->user_context);
|
||||
}
|
||||
/* Clean up timer and callback. */
|
||||
destroy_timer_callback(db->loop, callback_data);
|
||||
@@ -761,11 +766,15 @@ void redis_task_table_test_and_update_callback(redisAsyncContext *c,
|
||||
redisReply *reply = (redisReply *) r;
|
||||
/* Parse the task from the reply. */
|
||||
Task *task = parse_and_construct_task_from_redis_reply(reply);
|
||||
/* Determine whether the update happened. */
|
||||
auto message = flatbuffers::GetRoot<TaskReply>(reply->str);
|
||||
bool updated = message->updated();
|
||||
|
||||
/* Call the done callback if there is one. */
|
||||
task_table_get_callback done_callback =
|
||||
(task_table_get_callback) callback_data->done_callback;
|
||||
task_table_test_and_update_callback done_callback =
|
||||
(task_table_test_and_update_callback) callback_data->done_callback;
|
||||
if (done_callback != NULL) {
|
||||
done_callback(task, callback_data->user_context);
|
||||
done_callback(task, callback_data->user_context, updated);
|
||||
}
|
||||
/* Free the task if it is not NULL. */
|
||||
if (task != NULL) {
|
||||
|
||||
@@ -33,13 +33,14 @@ void task_table_update(DBHandle *db_handle,
|
||||
redis_task_table_update, user_context);
|
||||
}
|
||||
|
||||
void task_table_test_and_update(DBHandle *db_handle,
|
||||
TaskID task_id,
|
||||
int test_state_bitmask,
|
||||
int update_state,
|
||||
RetryInfo *retry,
|
||||
task_table_get_callback done_callback,
|
||||
void *user_context) {
|
||||
void task_table_test_and_update(
|
||||
DBHandle *db_handle,
|
||||
TaskID task_id,
|
||||
int test_state_bitmask,
|
||||
int update_state,
|
||||
RetryInfo *retry,
|
||||
task_table_test_and_update_callback done_callback,
|
||||
void *user_context) {
|
||||
TaskTableTestAndUpdateData *update_data =
|
||||
(TaskTableTestAndUpdateData *) malloc(sizeof(TaskTableTestAndUpdateData));
|
||||
update_data->test_state_bitmask = test_state_bitmask;
|
||||
|
||||
@@ -28,6 +28,13 @@ typedef void (*task_table_done_callback)(TaskID task_id, void *user_context);
|
||||
* was not in the task table, then the task pointer will be NULL. */
|
||||
typedef void (*task_table_get_callback)(Task *task, void *user_context);
|
||||
|
||||
/* Callback called when a task table test-and-update operation completes. If
|
||||
* the task ID was not in the task table, then the task pointer will be NULL.
|
||||
* If the update succeeded, the updated field will be set to true. */
|
||||
typedef void (*task_table_test_and_update_callback)(Task *task,
|
||||
void *user_context,
|
||||
bool updated);
|
||||
|
||||
/**
|
||||
* Get a task's entry from the task table.
|
||||
*
|
||||
@@ -107,13 +114,14 @@ void task_table_update(DBHandle *db_handle,
|
||||
* fail_callback.
|
||||
* @return Void.
|
||||
*/
|
||||
void task_table_test_and_update(DBHandle *db_handle,
|
||||
TaskID task_id,
|
||||
int test_state_bitmask,
|
||||
int update_state,
|
||||
RetryInfo *retry,
|
||||
task_table_get_callback done_callback,
|
||||
void *user_context);
|
||||
void task_table_test_and_update(
|
||||
DBHandle *db_handle,
|
||||
TaskID task_id,
|
||||
int test_state_bitmask,
|
||||
int update_state,
|
||||
RetryInfo *retry,
|
||||
task_table_test_and_update_callback done_callback,
|
||||
void *user_context);
|
||||
|
||||
/* Data that is needed to test and set the task's scheduling state. */
|
||||
typedef struct {
|
||||
|
||||
@@ -34,6 +34,7 @@ void new_object_fail_callback(UniqueID id,
|
||||
|
||||
void new_object_done_callback(ObjectID object_id,
|
||||
TaskID task_id,
|
||||
bool is_put,
|
||||
void *user_context) {
|
||||
new_object_succeeded = 1;
|
||||
CHECK(ObjectID_equal(object_id, new_object_id));
|
||||
@@ -60,7 +61,7 @@ void new_object_task_callback(TaskID task_id, void *user_context) {
|
||||
.fail_callback = new_object_fail_callback,
|
||||
};
|
||||
DBHandle *db = (DBHandle *) user_context;
|
||||
result_table_add(db, new_object_id, new_object_task_id, &retry,
|
||||
result_table_add(db, new_object_id, new_object_task_id, false, &retry,
|
||||
new_object_lookup_callback, (void *) db);
|
||||
}
|
||||
|
||||
@@ -95,6 +96,7 @@ TEST new_object_test(void) {
|
||||
|
||||
void new_object_no_task_callback(ObjectID object_id,
|
||||
TaskID task_id,
|
||||
bool is_put,
|
||||
void *user_context) {
|
||||
new_object_succeeded = 1;
|
||||
CHECK(IS_NIL_ID(task_id));
|
||||
|
||||
Reference in New Issue
Block a user