Skip to content

Commit 281c67f

Browse files
committed
Add resurrection test
Seems to be some problem on the platform?
1 parent 301e294 commit 281c67f

File tree

3 files changed

+55
-10
lines changed

3 files changed

+55
-10
lines changed

src/apify/storage_clients/_apify/_request_queue_client.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,12 @@ async def open(
157157
# a new storage ID after Actor's reboot or migration.
158158
id = configuration.default_request_queue_id
159159
case (None, name):
160-
# If name is provided, get or create the storage by name.
160+
# If only name is provided, get or create the storage by name.
161161
id = RequestQueueMetadata.model_validate(
162162
await apify_rqs_client.get_or_create(name=name),
163163
).id
164164
case (_, None):
165+
# If only id is provided, use it.
165166
pass
166167
case (_, _):
167168
# If both id and name are provided, raise an error.
@@ -170,7 +171,7 @@ async def open(
170171
raise RuntimeError('Unreachable code')
171172

172173
# Use suitable client_key to make `hadMultipleClients` response of Apify API useful.
173-
# It should persist across migrated Actor runs on the Apify platform.
174+
# It should persist across migrated or resurrected Actor runs on the Apify platform.
174175
_api_max_client_key_length = 32
175176
client_key = (configuration.actor_run_id or crypto_random_object_id(length=_api_max_client_key_length))[
176177
:_api_max_client_key_length

tests/integration/_utils.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
from __future__ import annotations
22

3-
from crawlee._utils.crypto import crypto_random_object_id
4-
53

64
def generate_unique_resource_name(label: str) -> str:
75
"""Generates a unique resource name, which will contain the given label."""
6+
name_template = 'python-sdk-tests-{}-generated-{}'
7+
template_length = len(name_template.format('', ''))
8+
api_name_limit = 63
9+
label_length_limit = api_name_limit - template_length
10+
811
label = label.replace('_', '-')
9-
return f'python-sdk-tests-{label}-generated-{crypto_random_object_id(8)}'
12+
assert len(label) <= label_length_limit, f'Max label length is {label_length_limit}, but got {len(label)}'
13+
14+
return name_template.format(label, template_length)

tests/integration/test_actor_request_queue.py

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from ._utils import generate_unique_resource_name
77
from apify import Actor, Request
8+
from apify._models import ActorRun
89

910
if TYPE_CHECKING:
1011
from apify_client import ApifyClientAsync
@@ -157,6 +158,7 @@ async def test_request_queue_had_multiple_clients_local(
157158

158159
# Check that it is correctly in the RequestQueueClient metadata
159160
assert (await request_queue_force_cloud.get_metadata()).had_multiple_clients is True
161+
160162
# Check that it is correctly in the API
161163
api_response = await api_client.get()
162164
assert api_response
@@ -174,6 +176,7 @@ async def test_request_queue_not_had_multiple_clients_local(
174176

175177
# Check that it is correctly in the RequestQueueClient metadata
176178
assert (await request_queue_force_cloud.get_metadata()).had_multiple_clients is False
179+
177180
# Check that it is correctly in the API
178181
api_client = apify_client_async.request_queue(request_queue_id=request_queue_force_cloud.id)
179182
api_response = await api_client.get()
@@ -185,8 +188,9 @@ async def test_request_queue_had_multiple_clients_platform(
185188
make_actor: MakeActorFunction,
186189
run_actor: RunActorFunction,
187190
) -> None:
191+
"""Test that `RequestQueue` clients created with different `client_key` appear as distinct clients."""
192+
188193
async def main() -> None:
189-
"""`RequestQueue` clients created with different `client_key` should appear as distinct clients."""
190194
from apify_client import ApifyClientAsync
191195

192196
async with Actor:
@@ -199,7 +203,6 @@ async def main() -> None:
199203
)
200204
await api_client.list_head()
201205

202-
# Check that it is correctly in the RequestQueueClient metadata
203206
assert (await rq_1.get_metadata()).had_multiple_clients is True
204207

205208
actor = await make_actor(label='rq-had-multiple-clients', main_func=main)
@@ -212,18 +215,54 @@ async def test_request_queue_not_had_multiple_clients_platform(
212215
make_actor: MakeActorFunction,
213216
run_actor: RunActorFunction,
214217
) -> None:
218+
"""Test that same `RequestQueue` created from Actor does not act as multiple clients."""
219+
215220
async def main() -> None:
216-
"""Test that same `RequestQueue` created from Actor does not act as multiple clients."""
217221
async with Actor:
218222
rq_1 = await Actor.open_request_queue()
219-
# Two calls to API to create situation where different `client_key` can set `had_multiple_clients` to True
223+
# Two calls to API to create situation where unset `client_key` can cause `had_multiple_clients` to True
220224
await rq_1.fetch_next_request()
221225
await rq_1.fetch_next_request()
222226

223-
# Check that it is correctly in the RequestQueueClient metadata
224227
assert (await rq_1.get_metadata()).had_multiple_clients is False
225228

226229
actor = await make_actor(label='rq-not-had-multiple-clients', main_func=main)
227230
run_result = await run_actor(actor)
228231

229232
assert run_result.status == 'SUCCEEDED'
233+
234+
235+
async def test_request_queue_not_had_multiple_clients_platform_resurrection(
236+
make_actor: MakeActorFunction,
237+
run_actor: RunActorFunction,
238+
apify_client_async: ApifyClientAsync,
239+
) -> None:
240+
"""Test `RequestQueue` created from Actor does not act as multiple clients even after resurrection."""
241+
242+
async def main() -> None:
243+
async with Actor:
244+
rq_1 = await Actor.open_request_queue()
245+
Actor.log.info(f'Used client key = {rq_1._client._api_client.client_key}, request queue ID = {rq_1.id}')
246+
metadata = await rq_1.get_metadata()
247+
Actor.log.info(metadata)
248+
249+
assert metadata.had_multiple_clients is False, 'Not accessed yet, should be False'
250+
251+
await rq_1.fetch_next_request()
252+
253+
assert (await rq_1.get_metadata()).had_multiple_clients is False, (
254+
'Accessed with same client, should be False'
255+
)
256+
257+
actor = await make_actor(label='rq-multiple-clients-resurrection', main_func=main)
258+
run_result = await run_actor(actor)
259+
assert run_result.status == 'SUCCEEDED'
260+
261+
# Resurrect the run, the RequestQueue should still use same client key and thus not have multiple clients.
262+
run_client = apify_client_async.run(run_id=run_result.id)
263+
# Redirect logs even from the resurrected run
264+
streamed_log = await run_client.get_streamed_log(from_start=False)
265+
await run_client.resurrect()
266+
async with streamed_log:
267+
run_result = ActorRun.model_validate(await run_client.wait_for_finish(wait_secs=600))
268+
assert run_result.status == 'SUCCEEDED'

0 commit comments

Comments
 (0)