@@ -134,6 +134,8 @@ class MockWorkerPool : public WorkerPoolInterface {
134134 int num_pops;
135135};
136136
137+ namespace {
138+
137139std::shared_ptr<ClusterResourceScheduler> CreateSingleNodeScheduler (
138140 const std::string &id, double num_cpus, gcs::GcsClient &gcs_client) {
139141 absl::flat_hash_map<std::string, double > local_node_resources;
@@ -151,7 +153,8 @@ std::shared_ptr<ClusterResourceScheduler> CreateSingleNodeScheduler(
151153}
152154
153155RayTask CreateTask (const std::unordered_map<std::string, double > &required_resources,
154- const std::string &task_name = " default" ) {
156+ const std::string &task_name = " default" ,
157+ const std::vector<std::unique_ptr<TaskArg>> &args = {}) {
155158 TaskSpecBuilder spec_builder;
156159 TaskID id = RandomTaskId ();
157160 JobID job_id = RandomJobId ();
@@ -181,9 +184,15 @@ RayTask CreateTask(const std::unordered_map<std::string, double> &required_resou
181184
182185 spec_builder.SetNormalTaskSpec (0 , false , " " , rpc::SchedulingStrategy (), ActorID::Nil ());
183186
187+ for (const auto &arg : args) {
188+ spec_builder.AddArg (*arg);
189+ }
190+
184191 return RayTask (std::move (spec_builder).ConsumeAndBuild ());
185192}
186193
194+ } // namespace
195+
187196class LocalTaskManagerTest : public ::testing::Test {
188197 public:
189198 explicit LocalTaskManagerTest (double num_cpus = 3.0 )
@@ -253,8 +262,6 @@ class LocalTaskManagerTest : public ::testing::Test {
253262};
254263
255264TEST_F (LocalTaskManagerTest, TestTaskDispatchingOrder) {
256- RAY_LOG (INFO ) << " Starting TestTaskDispatchingOrder" ;
257-
258265 // Initial setup: 3 CPUs available.
259266 std::shared_ptr<MockWorker> worker1 =
260267 std::make_shared<MockWorker>(WorkerID::FromRandom (), 0 );
@@ -270,28 +277,12 @@ TEST_F(LocalTaskManagerTest, TestTaskDispatchingOrder) {
270277 auto task_f1 = CreateTask ({{ray::kCPU_ResourceLabel , 1 }}, " f" );
271278 auto task_f2 = CreateTask ({{ray::kCPU_ResourceLabel , 1 }}, " f" );
272279 rpc::RequestWorkerLeaseReply reply;
273- bool callback_occurred = false ;
274- bool *callback_occurred_ptr = &callback_occurred;
275- auto callback = [callback_occurred_ptr](
276- Status, std::function<void ()>, std::function<void ()>) {
277- *callback_occurred_ptr = true ;
278- };
279280 local_task_manager_->WaitForTaskArgsRequests (std::make_shared<internal::Work>(
280- task_f1,
281- false ,
282- false ,
283- &reply,
284- [callback] { callback (Status::OK (), nullptr , nullptr ); },
285- internal::WorkStatus::WAITING ));
281+ task_f1, false , false , &reply, [] {}, internal::WorkStatus::WAITING ));
286282 local_task_manager_->ScheduleAndDispatchTasks ();
287283 pool_.TriggerCallbacks ();
288284 local_task_manager_->WaitForTaskArgsRequests (std::make_shared<internal::Work>(
289- task_f2,
290- false ,
291- false ,
292- &reply,
293- [callback] { callback (Status::OK (), nullptr , nullptr ); },
294- internal::WorkStatus::WAITING ));
285+ task_f2, false , false , &reply, [] {}, internal::WorkStatus::WAITING ));
295286 local_task_manager_->ScheduleAndDispatchTasks ();
296287 pool_.TriggerCallbacks ();
297288
@@ -301,40 +292,70 @@ TEST_F(LocalTaskManagerTest, TestTaskDispatchingOrder) {
301292 auto task_f5 = CreateTask ({{ray::kCPU_ResourceLabel , 1 }}, " f" );
302293 auto task_g1 = CreateTask ({{ray::kCPU_ResourceLabel , 1 }}, " g" );
303294 local_task_manager_->WaitForTaskArgsRequests (std::make_shared<internal::Work>(
304- task_f3,
305- false ,
306- false ,
307- &reply,
308- [callback] { callback (Status::OK (), nullptr , nullptr ); },
309- internal::WorkStatus::WAITING ));
295+ task_f3, false , false , &reply, [] {}, internal::WorkStatus::WAITING ));
310296 local_task_manager_->WaitForTaskArgsRequests (std::make_shared<internal::Work>(
311- task_f4,
312- false ,
313- false ,
314- &reply,
315- [callback] { callback (Status::OK (), nullptr , nullptr ); },
316- internal::WorkStatus::WAITING ));
297+ task_f4, false , false , &reply, [] {}, internal::WorkStatus::WAITING ));
317298 local_task_manager_->WaitForTaskArgsRequests (std::make_shared<internal::Work>(
318- task_f5,
319- false ,
320- false ,
321- &reply,
322- [callback] { callback (Status::OK (), nullptr , nullptr ); },
323- internal::WorkStatus::WAITING ));
299+ task_f5, false , false , &reply, [] {}, internal::WorkStatus::WAITING ));
324300 local_task_manager_->WaitForTaskArgsRequests (std::make_shared<internal::Work>(
325- task_g1,
326- false ,
327- false ,
328- &reply,
329- [callback] { callback (Status::OK (), nullptr , nullptr ); },
330- internal::WorkStatus::WAITING ));
301+ task_g1, false , false , &reply, [] {}, internal::WorkStatus::WAITING ));
331302 local_task_manager_->ScheduleAndDispatchTasks ();
332303 pool_.TriggerCallbacks ();
333304 auto tasks_to_dispatch_ = local_task_manager_->GetTaskToDispatch ();
334305 // Only task f in queue now as g is dispatched.
335306 ASSERT_EQ (tasks_to_dispatch_.size (), 1 );
336307}
337308
309+ TEST_F (LocalTaskManagerTest, TestNoLeakOnImpossibleInfeasibleTask) {
310+ // Note that ideally it shouldn't be possible for an infeasible task to
311+ // be in the local task manager when ScheduleAndDispatchTasks happens.
312+ // See https://github.com/ray-project/ray/pull/52295 for reasons why added this.
313+
314+ std::shared_ptr<MockWorker> worker1 =
315+ std::make_shared<MockWorker>(WorkerID::FromRandom (), 0 );
316+ std::shared_ptr<MockWorker> worker2 =
317+ std::make_shared<MockWorker>(WorkerID::FromRandom (), 0 );
318+ pool_.PushWorker (std::static_pointer_cast<WorkerInterface>(worker1));
319+
320+ // Create 2 tasks that requires 3 CPU's each and are waiting on an arg.
321+ auto arg_id = ObjectID::FromRandom ();
322+ std::vector<std::unique_ptr<TaskArg>> args;
323+ args.push_back (
324+ std::make_unique<TaskArgByReference>(arg_id, rpc::Address{}, " call_site" ));
325+ auto task1 = CreateTask ({{kCPU_ResourceLabel , 3 }}, " f" , args);
326+ auto task2 = CreateTask ({{kCPU_ResourceLabel , 3 }}, " f2" , args);
327+
328+ EXPECT_CALL (object_manager_, Pull (_, _, _))
329+ .WillOnce (::testing::Return (1 ))
330+ .WillOnce (::testing::Return (2 ));
331+
332+ // Submit the tasks to the local task manager.
333+ int num_callbacks_called = 0 ;
334+ auto callback = [&num_callbacks_called]() { ++num_callbacks_called; };
335+ rpc::RequestWorkerLeaseReply reply1;
336+ local_task_manager_->QueueAndScheduleTask (std::make_shared<internal::Work>(
337+ task1, false , false , &reply1, callback, internal::WorkStatus::WAITING ));
338+ rpc::RequestWorkerLeaseReply reply2;
339+ local_task_manager_->QueueAndScheduleTask (std::make_shared<internal::Work>(
340+ task2, false , false , &reply2, callback, internal::WorkStatus::WAITING ));
341+
342+ // Node no longer has cpu.
343+ scheduler_->GetLocalResourceManager ().DeleteLocalResource (
344+ scheduling::ResourceID::CPU ());
345+
346+ // Simulate arg becoming local.
347+ local_task_manager_->TasksUnblocked (
348+ {task1.GetTaskSpecification ().TaskId (), task2.GetTaskSpecification ().TaskId ()});
349+
350+ // Assert that the the correct rpc replies were sent back and the dispatch map is empty.
351+ ASSERT_EQ (reply1.failure_type (),
352+ rpc::RequestWorkerLeaseReply::SCHEDULING_CANCELLED_UNSCHEDULABLE );
353+ ASSERT_EQ (reply2.failure_type (),
354+ rpc::RequestWorkerLeaseReply::SCHEDULING_CANCELLED_UNSCHEDULABLE );
355+ ASSERT_EQ (num_callbacks_called, 2 );
356+ ASSERT_EQ (local_task_manager_->GetTaskToDispatch ().size (), 0 );
357+ }
358+
338359int main (int argc, char **argv) {
339360 ::testing::InitGoogleTest (&argc, argv);
340361 return RUN_ALL_TESTS ();
0 commit comments