[xray] Basic task reconstruction mechanism (#2526)

## What do these changes do?

This implements basic task reconstruction in raylet. There are two parts to this PR:
1. Reconstruction suppression through the `TaskReconstructionLog`. This prevents two raylets from reconstructing the same task if they decide simultaneously (via the logic in #2497) that reconstruction is necessary.
2. Task resubmission once a raylet becomes responsible for reconstructing a task.

Reconstruction is quite slow in this PR, especially for long chains of dependent tasks. This is mainly due to the lease table mechanism, where nodes may wait too long before trying to reconstruct a task. There are two ways to improve this:
1. Expire entries in the lease table using Redis `PEXPIRE`. This is a WIP and I may include it in this PR.
2. Introduce a "fast path" for reconstructing dependencies of a re-executed task. Normally, we wait for an initial timeout before checking whether a task requires reconstruction. However, if a task requires reconstruction, then it's likely that its dependencies also require reconstruction. In this case, we could skip the initial timeout before checking the GCS to see whether reconstruction is necessary (e.g., if the object has been evicted).

Since handling failures of other raylets is probably not yet complete in master, this only turns back on Python tests for reconstructing evicted objects.
This commit is contained in:
Stephanie Wang
2018-08-09 07:24:37 -07:00
committed by Robert Nishihara
parent 8ae82180b4
commit d49b4bef0a
17 changed files with 313 additions and 50 deletions
+7 -17
View File
@@ -152,7 +152,9 @@ def ray_start_reconstruction(request):
# Start the Redis global state store.
node_ip_address = "127.0.0.1"
redis_address, redis_shards = ray.services.start_redis(node_ip_address)
use_raylet = os.environ.get("RAY_USE_XRAY") == "1"
redis_address, redis_shards = ray.services.start_redis(
node_ip_address, use_raylet=use_raylet)
redis_ip_address = ray.services.get_ip_address(redis_address)
redis_port = ray.services.get_port(redis_address)
time.sleep(0.1)
@@ -221,9 +223,6 @@ def ray_start_reconstruction(request):
ray.shutdown()
@pytest.mark.skipif(
os.environ.get("RAY_USE_XRAY") == "1",
reason="This test does not work with xray yet.")
@pytest.mark.skipif(
os.environ.get("RAY_USE_NEW_GCS") == "on",
reason="Failing with new GCS API on Linux.")
@@ -232,7 +231,7 @@ def test_simple(ray_start_reconstruction):
# Define the size of one task's return argument so that the combined
# sum of all objects' sizes is at least twice the plasma stores'
# combined allotted memory.
num_objects = 1000
num_objects = 100
size = int(plasma_store_memory * 1.5 / (num_objects * 8))
# Define a remote task with no dependencies, which returns a numpy
@@ -265,9 +264,6 @@ def test_simple(ray_start_reconstruction):
del values
@pytest.mark.skipif(
os.environ.get("RAY_USE_XRAY") == "1",
reason="This test does not work with xray yet.")
@pytest.mark.skipif(
os.environ.get("RAY_USE_NEW_GCS") == "on",
reason="Failing with new GCS API on Linux.")
@@ -276,7 +272,7 @@ def test_recursive(ray_start_reconstruction):
# Define the size of one task's return argument so that the combined
# sum of all objects' sizes is at least twice the plasma stores'
# combined allotted memory.
num_objects = 1000
num_objects = 100
size = int(plasma_store_memory * 1.5 / (num_objects * 8))
# Define a root task with no dependencies, which returns a numpy array
@@ -324,9 +320,6 @@ def test_recursive(ray_start_reconstruction):
del values
@pytest.mark.skipif(
os.environ.get("RAY_USE_XRAY") == "1",
reason="This test does not work with xray yet.")
@pytest.mark.skipif(
os.environ.get("RAY_USE_NEW_GCS") == "on",
reason="Failing with new GCS API on Linux.")
@@ -335,7 +328,7 @@ def test_multiple_recursive(ray_start_reconstruction):
# Define the size of one task's return argument so that the combined
# sum of all objects' sizes is at least twice the plasma stores'
# combined allotted memory.
num_objects = 1000
num_objects = 100
size = plasma_store_memory * 2 // (num_objects * 8)
# Define a root task with no dependencies, which returns a numpy array
@@ -466,9 +459,6 @@ def test_nondeterministic_task(ray_start_reconstruction):
for error in errors)
@pytest.mark.skipif(
os.environ.get("RAY_USE_XRAY") == "1",
reason="This test does not work with xray yet.")
@pytest.mark.skipif(
os.environ.get("RAY_USE_NEW_GCS") == "on",
reason="Failing with new GCS API on Linux.")
@@ -477,7 +467,7 @@ def test_driver_put_errors(ray_start_reconstruction):
# Define the size of one task's return argument so that the combined
# sum of all objects' sizes is at least twice the plasma stores'
# combined allotted memory.
num_objects = 1000
num_objects = 100
size = plasma_store_memory * 2 // (num_objects * 8)
# Define a task with a single dependency, a numpy array, that returns