[core] Replace task resubmission in raylet with ownership protocol (#9394)

* Add intended worker ID to GetObjectStatus, tests

* Remove TaskID owner_id

* lint

* Add owner address to task args

* Make TaskArg a virtual class, remove multi args

* Set owner address for task args

* merge

* Fix tests

* Add ObjectRefs to task dependency manager, pass from task spec args

* tmp

* tmp

* Fix

* Add ownership info for task arguments

* Convert WaitForDirectActorCallArgs

* lint

* build

* update

* build

* java

* Move code

* build

* Revert "Fix Google log directory again (#9063)"

This reverts commit 275da2e400.

* Fix free

* Regression tests - shorten timeouts in reconstruction unit tests

* Remove timeout for non-actor tasks

* Modify tests using ray.internal.free

* Clean up future resolution code

* Raylet polls the owner

* todo

* comment

* Update src/ray/core_worker/core_worker.cc

Co-authored-by: Edward Oakes <ed.nmi.oakes@gmail.com>

* Drop stale actor table notifications

* Fix bug where actor restart hangs

* Revert buggy code for duplicate tasks

* build

* Fix errors for lru_evict and internal.free

* Revert "Drop stale actor table notifications"

This reverts commit 193c5d20e5577befd43f166e16c972e2f9247c91.

* Revert "build"

This reverts commit 5644edbac906ff6ef98feb40b6f62c9e63698c29.

* Fix free test

* Fixes for freed objects

Co-authored-by: Edward Oakes <ed.nmi.oakes@gmail.com>
This commit is contained in:
Stephanie Wang
2020-07-15 14:55:51 -07:00
committed by GitHub
parent 5a40299d42
commit 4e81804cba
15 changed files with 239 additions and 107 deletions
+3 -3
View File
@@ -30,7 +30,7 @@ def memory_summary():
def free(object_refs, local_only=False, delete_creating_tasks=False):
"""Free a list of IDs from object stores.
"""Free a list of IDs from the in-process and plasma object stores.
This function is a low-level API which should be used in restricted
scenarios.
@@ -38,8 +38,8 @@ def free(object_refs, local_only=False, delete_creating_tasks=False):
If local_only is false, the request will be send to all object stores.
This method will not return any value to indicate whether the deletion is
successful or not. This function is an instruction to object store. If
the some of the objects are in use, object stores will delete them later
successful or not. This function is an instruction to the object store. If
some of the objects are in use, the object stores will delete them later
when the ref count is down to 0.
Examples:
+3 -2
View File
@@ -33,11 +33,12 @@ def test_internal_free(shutdown_only):
sampler = Sampler.remote()
# Free does not delete from in-memory store.
# Free deletes from in-memory store.
obj_ref = sampler.sample.remote()
ray.get(obj_ref)
ray.internal.free(obj_ref)
assert ray.get(obj_ref) == [1, 2, 3, 4, 5]
with pytest.raises(Exception):
ray.get(obj_ref)
# Free deletes big objects from plasma store.
big_id = sampler.sample_big.remote()
+4 -2
View File
@@ -6,13 +6,15 @@ import pytest
import ray
from ray.exceptions import RayCancellationError, RayTaskError, \
RayTimeoutError, RayWorkerError
RayTimeoutError, RayWorkerError, \
UnreconstructableError
from ray.test_utils import SignalActor
def valid_exceptions(use_force):
if use_force:
return (RayTaskError, RayCancellationError, RayWorkerError)
return (RayTaskError, RayCancellationError, RayWorkerError,
UnreconstructableError)
else:
return (RayTaskError, RayCancellationError)
-4
View File
@@ -821,8 +821,6 @@ def test_raylet_crash_when_get(ray_start_regular):
object_ref = ray.put(np.zeros(200 * 1024, dtype=np.uint8))
ray.internal.free(object_ref)
while ray.worker.global_worker.core_worker.object_exists(object_ref):
time.sleep(1)
thread = threading.Thread(target=sleep_to_kill_raylet)
thread.start()
@@ -984,8 +982,6 @@ def test_eviction(ray_start_cluster):
assert (isinstance(ray.get(obj), np.ndarray))
# Evict the object.
ray.internal.free([obj])
while ray.worker.global_worker.core_worker.object_exists(obj):
time.sleep(1)
# ray.get throws an exception.
with pytest.raises(ray.exceptions.UnreconstructableError):
ray.get(obj)
+7
View File
@@ -13,6 +13,7 @@ def test_cached_object(ray_start_cluster):
config = json.dumps({
"num_heartbeats_timeout": 10,
"raylet_heartbeat_timeout_milliseconds": 100,
"initial_reconstruction_timeout_milliseconds": 200,
})
cluster = ray_start_cluster
# Head node with no resources.
@@ -56,6 +57,7 @@ def test_reconstruction_cached_dependency(ray_start_cluster,
"raylet_heartbeat_timeout_milliseconds": 100,
"lineage_pinning_enabled": 1 if reconstruction_enabled else 0,
"free_objects_period_milliseconds": -1,
"initial_reconstruction_timeout_milliseconds": 200,
})
cluster = ray_start_cluster
# Head node with no resources.
@@ -109,6 +111,7 @@ def test_basic_reconstruction(ray_start_cluster, reconstruction_enabled):
"raylet_heartbeat_timeout_milliseconds": 100,
"lineage_pinning_enabled": 1 if reconstruction_enabled else 0,
"free_objects_period_milliseconds": -1,
"initial_reconstruction_timeout_milliseconds": 200,
})
cluster = ray_start_cluster
# Head node with no resources.
@@ -152,6 +155,7 @@ def test_basic_reconstruction_put(ray_start_cluster, reconstruction_enabled):
"raylet_heartbeat_timeout_milliseconds": 100,
"lineage_pinning_enabled": 1 if reconstruction_enabled else 0,
"free_objects_period_milliseconds": -1,
"initial_reconstruction_timeout_milliseconds": 200,
})
cluster = ray_start_cluster
# Head node with no resources.
@@ -198,6 +202,7 @@ def test_multiple_downstream_tasks(ray_start_cluster, reconstruction_enabled):
"raylet_heartbeat_timeout_milliseconds": 100,
"lineage_pinning_enabled": 1 if reconstruction_enabled else 0,
"free_objects_period_milliseconds": -1,
"initial_reconstruction_timeout_milliseconds": 200,
})
cluster = ray_start_cluster
# Head node with no resources.
@@ -252,6 +257,7 @@ def test_reconstruction_chain(ray_start_cluster, reconstruction_enabled):
"raylet_heartbeat_timeout_milliseconds": 100,
"lineage_pinning_enabled": 1 if reconstruction_enabled else 0,
"free_objects_period_milliseconds": -1,
"initial_reconstruction_timeout_milliseconds": 200,
})
cluster = ray_start_cluster
# Head node with no resources.
@@ -298,6 +304,7 @@ def test_reconstruction_stress(ray_start_cluster):
"free_objects_period_milliseconds": -1,
"max_direct_call_object_size": 100,
"task_retry_delay_ms": 100,
"initial_reconstruction_timeout_milliseconds": 200,
})
cluster = ray_start_cluster
# Head node with no resources.
+39 -24
View File
@@ -875,6 +875,7 @@ Status CoreWorker::Seal(const ObjectID &object_id, bool pin_object,
}));
} else {
RAY_RETURN_NOT_OK(plasma_store_provider_->Release(object_id));
reference_counter_->FreePlasmaObjects({object_id});
}
RAY_CHECK(memory_store_->Put(RayObject(rpc::ErrorType::OBJECT_IN_PLASMA), object_id));
return Status::OK();
@@ -1070,8 +1071,16 @@ Status CoreWorker::Delete(const std::vector<ObjectID> &object_ids, bool local_on
// logged and the object will not get released.
reference_counter_->FreePlasmaObjects(object_ids);
// Store an error in the in-memory store to indicate that the plasma value is
// no longer reachable.
memory_store_->Delete(object_ids);
for (const auto &object_id : object_ids) {
RAY_CHECK(memory_store_->Put(RayObject(rpc::ErrorType::OBJECT_UNRECONSTRUCTABLE),
object_id));
}
// We only delete from plasma, which avoids hangs (issue #7105). In-memory
// objects are always handled by ref counting only.
// objects can only be deleted once the ref count goes to 0.
absl::flat_hash_set<ObjectID> plasma_object_ids(object_ids.begin(), object_ids.end());
return plasma_store_provider_->Delete(plasma_object_ids, local_only,
delete_creating_tasks);
@@ -1708,31 +1717,37 @@ void CoreWorker::HandleGetObjectStatus(const rpc::GetObjectStatusRequest &reques
ObjectID object_id = ObjectID::FromBinary(request.object_id());
RAY_LOG(DEBUG) << "Received GetObjectStatus " << object_id;
// We own the task. Reply back to the borrower once the object has been
// created.
// TODO(swang): We could probably just send the object value if it is small
// enough and we have it local.
reply->set_status(rpc::GetObjectStatusReply::CREATED);
if (task_manager_->IsTaskPending(object_id.TaskId())) {
// Acquire a reference and retry. This prevents the object from being
// evicted out from under us before we can start the get.
AddLocalReference(object_id, "<temporary (get object status)>");
if (task_manager_->IsTaskPending(object_id.TaskId())) {
// The task is pending. Send the reply once the task finishes.
memory_store_->GetAsync(object_id,
[send_reply_callback](std::shared_ptr<RayObject> obj) {
send_reply_callback(Status::OK(), nullptr, nullptr);
});
RemoveLocalReference(object_id);
} else {
// We lost the race, the task is done.
RemoveLocalReference(object_id);
send_reply_callback(Status::OK(), nullptr, nullptr);
}
} else {
// The task is done. Send the reply immediately.
// Acquire a reference to the object. This prevents the object from being
// evicted out from under us while we check the object status and start the
// Get.
AddLocalReference(object_id, "<temporary (get object status)>");
rpc::Address owner_address;
auto has_owner = reference_counter_->GetOwner(object_id, &owner_address);
if (!has_owner) {
// We owned this object, but the object has gone out of scope.
reply->set_status(rpc::GetObjectStatusReply::OUT_OF_SCOPE);
send_reply_callback(Status::OK(), nullptr, nullptr);
} else {
RAY_CHECK(owner_address.worker_id() == request.owner_worker_id());
if (reference_counter_->IsPlasmaObjectFreed(object_id)) {
reply->set_status(rpc::GetObjectStatusReply::FREED);
} else {
reply->set_status(rpc::GetObjectStatusReply::CREATED);
}
// Send the reply once the value has become available. The value is
// guaranteed to become available eventually because we own the object and
// its ref count is > 0.
// TODO(swang): We could probably just send the object value if it is small
// enough and we have it local.
memory_store_->GetAsync(object_id,
[send_reply_callback](std::shared_ptr<RayObject> obj) {
send_reply_callback(Status::OK(), nullptr, nullptr);
});
}
RemoveLocalReference(object_id);
}
void CoreWorker::HandleWaitForActorOutOfScope(
+15 -5
View File
@@ -42,11 +42,21 @@ void FutureResolver::ResolveFutureAsync(const ObjectID &object_id,
RAY_LOG(WARNING) << "Error retrieving the value of object ID " << object_id
<< " that was deserialized: " << status.ToString();
}
// Either the owner is gone or the owner replied that the object has
// been created. In both cases, we can now try to fetch the object via
// plasma.
RAY_UNUSED(in_memory_store_->Put(RayObject(rpc::ErrorType::OBJECT_IN_PLASMA),
object_id));
if (!status.ok() || reply.status() == rpc::GetObjectStatusReply::OUT_OF_SCOPE) {
// The owner is gone or the owner replied that the object has gone
// out of scope (this is an edge case in the distributed ref counting
// protocol where a borrower dies before it can notify the owner of
// another borrower). Store an error so that an exception will be
// thrown immediately when the worker tries to get the value.
RAY_UNUSED(in_memory_store_->Put(
RayObject(rpc::ErrorType::OBJECT_UNRECONSTRUCTABLE), object_id));
} else {
// We can now try to fetch the object via plasma. If the owner later
// fails or the object is released, the raylet will eventually store
// an error in plasma on our behalf.
RAY_UNUSED(in_memory_store_->Put(RayObject(rpc::ErrorType::OBJECT_IN_PLASMA),
object_id));
}
}));
}
+5
View File
@@ -368,6 +368,11 @@ std::vector<rpc::Address> ReferenceCounter::GetOwnerAddresses(
return owner_addresses;
}
bool ReferenceCounter::IsPlasmaObjectFreed(const ObjectID &object_id) const {
absl::MutexLock lock(&mutex_);
return freed_objects_.find(object_id) != freed_objects_.end();
}
void ReferenceCounter::FreePlasmaObjects(const std::vector<ObjectID> &object_ids) {
absl::MutexLock lock(&mutex_);
for (const ObjectID &object_id : object_ids) {
+6
View File
@@ -208,6 +208,12 @@ class ReferenceCounter : public ReferenceCounterInterface {
std::vector<rpc::Address> GetOwnerAddresses(
const std::vector<ObjectID> object_ids) const;
/// Check whether an object value has been freed.
///
/// \param[in] object_id The object to check.
/// \return Whether the object value has been freed.
bool IsPlasmaObjectFreed(const ObjectID &object_id) const;
/// Release the underlying value from plasma (if any) for these objects.
///
/// \param[in] object_ids The IDs whose values to free.
@@ -2028,26 +2028,33 @@ TEST_F(ReferenceCountTest, TestFree) {
// Test free before receiving information about where the object is pinned.
rc->AddOwnedObject(id, {}, rpc::Address(), "", 0, true);
ASSERT_FALSE(rc->IsPlasmaObjectFreed(id));
rc->AddLocalReference(id, "");
rc->FreePlasmaObjects({id});
ASSERT_TRUE(rc->IsPlasmaObjectFreed(id));
ASSERT_FALSE(rc->SetDeleteCallback(id, callback));
ASSERT_EQ(deleted->count(id), 0);
rc->UpdateObjectPinnedAtRaylet(id, node_id);
bool pinned = true;
ASSERT_TRUE(rc->IsPlasmaObjectPinned(id, &pinned));
ASSERT_FALSE(pinned);
ASSERT_TRUE(rc->IsPlasmaObjectFreed(id));
rc->RemoveLocalReference(id, nullptr);
ASSERT_FALSE(rc->IsPlasmaObjectFreed(id));
// Test free after receiving information about where the object is pinned.
rc->AddOwnedObject(id, {}, rpc::Address(), "", 0, true);
rc->AddLocalReference(id, "");
ASSERT_TRUE(rc->SetDeleteCallback(id, callback));
rc->UpdateObjectPinnedAtRaylet(id, node_id);
ASSERT_FALSE(rc->IsPlasmaObjectFreed(id));
rc->FreePlasmaObjects({id});
ASSERT_TRUE(rc->IsPlasmaObjectFreed(id));
ASSERT_TRUE(deleted->count(id) > 0);
ASSERT_TRUE(rc->IsPlasmaObjectPinned(id, &pinned));
ASSERT_FALSE(pinned);
rc->RemoveLocalReference(id, nullptr);
ASSERT_FALSE(rc->IsPlasmaObjectFreed(id));
}
} // namespace ray
+4 -4
View File
@@ -822,12 +822,12 @@ TEST_F(SingleNodeTest, TestObjectInterface) {
// wait for objects being deleted, so wait a while for plasma store
// to process the command.
usleep(200 * 1000);
ASSERT_TRUE(core_worker.Get(ids, 0, &results).IsTimedOut());
ASSERT_TRUE(core_worker.Get(ids, 0, &results).ok());
// Since array2 has been deleted from the plasma store, the Get should
// timeout and return nullptr for all results.
// return UnreconstructableError for all results.
ASSERT_EQ(results.size(), 2);
ASSERT_TRUE(!results[0]);
ASSERT_TRUE(!results[1]);
ASSERT_TRUE(results[0]->IsException());
ASSERT_TRUE(results[1]->IsException());
}
TEST_F(SingleNodeTest, TestNormalTaskLocal) {
+1
View File
@@ -154,6 +154,7 @@ message GetObjectStatusReply {
enum ObjectStatus {
CREATED = 0;
OUT_OF_SCOPE = 1;
FREED = 2;
}
ObjectStatus status = 1;
}
+117 -62
View File
@@ -2257,6 +2257,7 @@ void NodeManager::MarkObjectsAsFailed(const ErrorType &error_type,
status = store_client_.Seal(object_id);
}
if (!status.ok() && !status.IsObjectExists()) {
RAY_LOG(INFO) << "Marking plasma object failed " << object_id;
// If we failed to save the error code, log a warning and push an error message
// to the driver.
std::ostringstream stream;
@@ -2759,7 +2760,7 @@ bool NodeManager::FinishAssignedTask(Worker &worker) {
}
} else {
// (See design_docs/task_states.rst for the state transition diagram.)
RAY_CHECK(local_queues_.RemoveTask(task_id, &task));
RAY_CHECK(local_queues_.RemoveTask(task_id, &task)) << task_id;
// Release task's resources. The worker's lifetime resources are still held.
auto const &task_resources = worker.GetTaskResourceIds();
@@ -3022,76 +3023,130 @@ void NodeManager::FinishAssignedActorCreationTask(const ActorID &parent_actor_id
void NodeManager::HandleTaskReconstruction(const TaskID &task_id,
const ObjectID &required_object_id) {
// Retrieve the task spec in order to re-execute the task.
RAY_CHECK_OK(gcs_client_->Tasks().AsyncGet(
task_id,
/*callback=*/
[this, required_object_id, task_id](
Status status, const boost::optional<TaskTableData> &task_data) {
if (task_data) {
// The task was in the GCS task table. Use the stored task spec to
// re-execute the task.
ResubmitTask(Task(task_data->task()), required_object_id);
return;
}
// The task was not in the GCS task table. It must therefore be in the
// lineage cache.
if (lineage_cache_.ContainsTask(task_id)) {
// Use a copy of the cached task spec to re-execute the task.
const Task task = lineage_cache_.GetTaskOrDie(task_id);
ResubmitTask(task, required_object_id);
} else {
RAY_LOG(WARNING)
<< "Metadata of task " << task_id
<< " not found in either GCS or lineage cache. It may have been evicted "
<< "by the redis LRU configuration. Consider increasing the memory "
"allocation via "
<< "ray.init(redis_max_memory=<max_memory_bytes>).";
MarkObjectsAsFailed(ErrorType::OBJECT_UNRECONSTRUCTABLE, {required_object_id},
JobID::Nil());
}
}));
// Get the owner's address.
rpc::Address owner_addr;
bool has_owner =
task_dependency_manager_.GetOwnerAddress(required_object_id, &owner_addr);
if (has_owner) {
if (!RayConfig::instance().object_pinning_enabled()) {
// LRU eviction is enabled. The object may still be in scope, but we
// weren't able to fetch the value within the timeout, so the value has
// most likely been evicted. Mark the object as unreachable.
MarkObjectsAsFailed(ErrorType::OBJECT_UNRECONSTRUCTABLE, {required_object_id},
JobID::Nil());
} else {
RAY_LOG(DEBUG) << "Required object " << required_object_id
<< " fetch timed out, asking owner "
<< WorkerID::FromBinary(owner_addr.worker_id());
// The owner's address exists. Poll the owner to check if the object is
// still in scope. If not, mark the object as failed.
// TODO(swang): If the owner has died, we could also mark the object as
// failed as soon as we hear about the owner's failure from the GCS,
// avoiding the raylet's reconstruction timeout.
auto client = std::unique_ptr<rpc::CoreWorkerClient>(
new rpc::CoreWorkerClient(owner_addr, client_call_manager_));
rpc::GetObjectStatusRequest request;
request.set_object_id(required_object_id.Binary());
request.set_owner_worker_id(owner_addr.worker_id());
RAY_CHECK_OK(client->GetObjectStatus(
request, [this, required_object_id](Status status,
const rpc::GetObjectStatusReply &reply) {
if (!status.ok() ||
reply.status() == rpc::GetObjectStatusReply::OUT_OF_SCOPE ||
reply.status() == rpc::GetObjectStatusReply::FREED) {
// The owner is gone, or the owner replied that the object has
// gone out of scope (this is an edge case in the distributed ref
// counting protocol where a borrower dies before it can notify
// the owner of another borrower), or the object value has been
// freed. Store an error in the local plasma store so that an
// exception will be thrown when the worker tries to get the
// value.
MarkObjectsAsFailed(ErrorType::OBJECT_UNRECONSTRUCTABLE,
{required_object_id}, JobID::Nil());
}
// Do nothing if the owner replied that the object is available. The
// object manager will continue trying to fetch the object, and this
// handler will get triggered again if the object is still
// unavailable after another timeout.
}));
}
} else {
// We do not have the owner's address. This is either an actor creation
// task or a randomly generated ObjectID. Try to look up the spec for the
// actor creation task.
// TODO(swang): The task lookup is only needed when the GCS actor service is
// disabled. Once the GCS actor service is enabled by default, we can
// immediately mark the object as failed if there is no ownership
// information.
RAY_LOG(DEBUG) << "Required object " << required_object_id
<< " fetch timed out, checking task table";
RAY_CHECK_OK(
gcs_client_->Tasks().AsyncGet(
task_id,
/*callback=*/
[this, required_object_id, task_id](
Status status, const boost::optional<TaskTableData> &task_data) {
if (task_data) {
// The task was in the GCS task table. Use the stored task spec to
// re-execute the task.
ResubmitTask(Task(task_data->task()), required_object_id);
return;
}
// The task was not in the GCS task table. It must therefore be in the
// lineage cache.
if (lineage_cache_.ContainsTask(task_id)) {
// Use a copy of the cached task spec to re-execute the task.
const Task task = lineage_cache_.GetTaskOrDie(task_id);
ResubmitTask(task, required_object_id);
} else {
// No actor creation task spec was found. This is most likely a
// randomly generated ObjectID whose value is unreachable. Mark the
// object as failed.
RAY_LOG(WARNING)
<< "Ray cannot get the value of ObjectIDs that are generated "
"randomly (ObjectID.from_random()) or out-of-band "
"(ObjectID.from_binary(...)) because Ray "
"does not know which task will create them. "
"If this was not how your object ID was generated, please file an "
"issue "
"at https://github.com/ray-project/ray/issues/";
MarkObjectsAsFailed(ErrorType::OBJECT_UNRECONSTRUCTABLE,
{required_object_id}, JobID::Nil());
}
}));
}
}
void NodeManager::ResubmitTask(const Task &task, const ObjectID &required_object_id) {
RAY_LOG(DEBUG) << "Attempting to resubmit task "
<< task.GetTaskSpecification().TaskId();
// Actors should only be recreated if the first initialization failed or if
// the most recent instance of the actor failed.
if (task.GetTaskSpecification().IsActorCreationTask()) {
const auto &actor_id = task.GetTaskSpecification().ActorCreationId();
const auto it = actor_registry_.find(actor_id);
if (it != actor_registry_.end() && it->second.GetState() == ActorTableData::ALIVE) {
// If the actor is still alive, then do not resubmit the task. If the
// actor actually is dead and a result is needed, then reconstruction
// for this task will be triggered again.
RAY_LOG(WARNING)
<< "Actor creation task resubmitted, but the actor is still alive.";
return;
}
}
// Driver tasks cannot be reconstructed. If this is a driver task, push an
// error to the driver and do not resubmit it.
if (task.GetTaskSpecification().IsDriverTask()) {
// TODO(rkn): Define this constant somewhere else.
std::string type = "put_reconstruction";
std::ostringstream error_message;
error_message << "The task with ID " << task.GetTaskSpecification().TaskId()
<< " is a driver task and so the object created by ray.put "
<< "could not be reconstructed.";
auto error_data_ptr =
gcs::CreateErrorTableData(type, error_message.str(), current_time_ms(),
task.GetTaskSpecification().JobId());
RAY_CHECK_OK(gcs_client_->Errors().AsyncReportJobError(error_data_ptr, nullptr));
MarkObjectsAsFailed(ErrorType::OBJECT_UNRECONSTRUCTABLE, {required_object_id},
task.GetTaskSpecification().JobId());
// All failure handling is handled by the owner, except for actor creation
// tasks.
if (!task.GetTaskSpecification().IsActorCreationTask()) {
return;
}
RAY_LOG(INFO) << "Resubmitting task " << task.GetTaskSpecification().TaskId()
<< " on node " << self_node_id_;
// When the GCS is disabled, the raylet is responsible for restarting the actor.
if (RayConfig::instance().gcs_actor_service_enabled()) {
return;
}
// Actors should only be recreated if the first initialization failed or if
// the most recent instance of the actor failed.
const auto &actor_id = task.GetTaskSpecification().ActorCreationId();
const auto it = actor_registry_.find(actor_id);
if (it != actor_registry_.end() && it->second.GetState() == ActorTableData::ALIVE) {
// If the actor is still alive, then do not resubmit the task. If the
// actor actually is dead and a result is needed, then reconstruction
// for this task will be triggered again.
RAY_LOG(WARNING) << "Actor creation task resubmitted, but the actor is still alive.";
return;
}
RAY_LOG(INFO) << "Resubmitting actor creation task "
<< task.GetTaskSpecification().TaskId() << " on node " << self_node_id_;
// The task may be reconstructed. Submit it with an empty lineage, since any
// uncommitted lineage must already be in the lineage cache. At this point,
// the task should not yet exist in the local scheduling queue. If it does,
+17 -1
View File
@@ -92,7 +92,7 @@ std::vector<TaskID> TaskDependencyManager::HandleObjectLocal(
const ray::ObjectID &object_id) {
// Add the object to the table of locally available objects.
auto inserted = local_objects_.insert(object_id);
RAY_CHECK(inserted.second);
RAY_CHECK(inserted.second) << object_id;
// Find all tasks and workers that depend on the newly available object.
std::vector<TaskID> ready_task_ids;
@@ -513,6 +513,22 @@ void TaskDependencyManager::RecordMetrics() const {
pending_tasks_.size(), {{stats::ValueTypeKey, "num_pending_tasks"}});
}
bool TaskDependencyManager::GetOwnerAddress(const ObjectID &object_id,
rpc::Address *owner_address) const {
const auto creating_task_entry = required_tasks_.find(object_id.TaskId());
if (creating_task_entry == required_tasks_.end()) {
return false;
}
const auto it = creating_task_entry->second.find(object_id);
if (it == creating_task_entry->second.end()) {
return false;
}
*owner_address = it->second.owner_address;
return !owner_address->worker_id().empty();
}
} // namespace raylet
} // namespace ray
+11
View File
@@ -164,6 +164,17 @@ class TaskDependencyManager {
/// Record metrics.
void RecordMetrics() const;
/// Get the address of the owner of this object. An address will only be
/// returned if the caller previously specified that this object is required
/// on this node, through a call to SubscribeGetDependencies or
/// SubscribeWaitDependencies.
///
/// \param[in] object_id The object whose owner to get.
/// \param[out] owner_address The address of the object's owner, if
/// available.
/// \return True if we have owner information for the object.
bool GetOwnerAddress(const ObjectID &object_id, rpc::Address *owner_address) const;
private:
struct ObjectDependencies {
ObjectDependencies(const rpc::ObjectReference &ref)