0
# Run Management
1
2
Actor run lifecycle management including execution monitoring, result access, and advanced operations like metamorphosis, resurrection, and real-time status tracking.
3
4
## Capabilities
5
6
### Run Operations
7
8
Individual run management with comprehensive lifecycle control and monitoring capabilities.
9
10
```python { .api }
11
class RunClient:
12
def get(self) -> dict | None:
13
"""Get run information including status, statistics, and metadata."""
14
15
def update(
16
self,
17
*,
18
status_message: str | None = None,
19
is_status_message_terminal: bool | None = None,
20
general_access: RunGeneralAccess | None = None
21
) -> dict:
22
"""Update run configuration.
23
24
Args:
25
status_message: Custom status message
26
is_status_message_terminal: Whether status message is final
27
general_access: Run access level (from apify_shared.consts)
28
"""
29
30
def delete(self) -> None:
31
"""Delete run and its associated data."""
32
33
def abort(self, *, gracefully: bool | None = None) -> dict:
34
"""Abort running Actor run.
35
36
Args:
37
gracefully: Whether to allow graceful shutdown
38
"""
39
40
def wait_for_finish(self, *, wait_secs: int | None = None) -> dict | None:
41
"""Wait for run completion.
42
43
Args:
44
wait_secs: Maximum wait time in seconds
45
"""
46
47
def metamorph(self, *, target_actor_id: str, **kwargs) -> dict:
48
"""Transform run into different Actor.
49
50
Args:
51
target_actor_id: ID of target Actor
52
build: Target build ID
53
content_type: Input content type
54
**kwargs: Additional metamorphosis parameters
55
"""
56
57
def resurrect(self, **kwargs) -> dict:
58
"""Resurrect finished run with same configuration.
59
60
Args:
61
build: Build ID to use
62
memory: Memory allocation
63
timeout: Run timeout
64
**kwargs: Additional run parameters
65
"""
66
67
def reboot(self) -> dict:
68
"""Reboot running Actor run."""
69
70
def dataset(self) -> DatasetClient:
71
"""Get default dataset client for this run."""
72
73
def key_value_store(self) -> KeyValueStoreClient:
74
"""Get default key-value store client for this run."""
75
76
def request_queue(self) -> RequestQueueClient:
77
"""Get default request queue client for this run."""
78
79
def log(self) -> LogClient:
80
"""Get log client for this run."""
81
82
def get_streamed_log(self, **kwargs) -> StreamedLog:
83
"""Get streamed log instance for real-time log monitoring.
84
85
Args:
86
**kwargs: Streaming configuration parameters
87
"""
88
89
def charge(self, event_name: str, **kwargs) -> None:
90
"""Charge for pay-per-event run.
91
92
Args:
93
event_name: Name of billable event
94
**kwargs: Event parameters
95
"""
96
97
def get_status_message_watcher(self, **kwargs) -> StatusMessageWatcher:
98
"""Get status message watcher for real-time status updates.
99
100
Args:
101
**kwargs: Watcher configuration parameters
102
"""
103
104
class RunClientAsync:
105
"""Async version of RunClient with identical methods."""
106
107
class RunCollectionClient:
108
def list(
109
self,
110
*,
111
limit: int | None = None,
112
offset: int | None = None,
113
desc: bool | None = None,
114
status: str | None = None
115
) -> ListPage[dict]:
116
"""List Actor runs.
117
118
Args:
119
limit: Maximum number of runs
120
offset: Pagination offset
121
desc: Sort in descending order
122
status: Filter by run status
123
"""
124
125
class RunCollectionClientAsync:
126
"""Async version of RunCollectionClient with identical methods."""
127
```
128
129
### Real-time Monitoring Types
130
131
```python { .api }
132
class StreamedLog:
133
"""Real-time log streaming interface."""
134
135
def __iter__(self) -> Iterator[str]:
136
"""Iterate over log lines in real-time."""
137
138
def close(self) -> None:
139
"""Close log stream."""
140
141
class StatusMessageWatcher:
142
"""Real-time status message monitoring interface."""
143
144
def __iter__(self) -> Iterator[dict]:
145
"""Iterate over status message updates."""
146
147
def close(self) -> None:
148
"""Close status watcher."""
149
```
150
151
## Usage Examples
152
153
### Basic Run Management
154
155
```python
156
from apify_client import ApifyClient
157
158
client = ApifyClient('your-api-token')
159
160
# Start Actor and get run
161
actor = client.actor('john-doe/web-scraper')
162
run = actor.start(run_input={'startUrls': ['https://example.com']})
163
164
# Monitor run progress
165
run_client = client.run(run['id'])
166
run_info = run_client.get()
167
168
print(f"Run status: {run_info['status']}")
169
print(f"Started at: {run_info['startedAt']}")
170
171
# Wait for completion
172
final_run = run_client.wait_for_finish(wait_secs=300)
173
if final_run:
174
print(f"Run finished with status: {final_run['status']}")
175
print(f"Usage: {final_run['stats']}")
176
else:
177
print("Run timeout reached")
178
```
179
180
### Advanced Run Operations
181
182
```python
183
# Metamorphosis - transform running Actor into different Actor
184
run_client = client.run('current-run-id')
185
186
# Transform to data processing Actor
187
new_run = run_client.metamorph(
188
target_actor_id='data-processor/clean-data',
189
run_input={'source': 'transformed_data'}
190
)
191
192
print(f"Metamorphosed to new run: {new_run['id']}")
193
194
# Resurrect failed run
195
failed_run = client.run('failed-run-id')
196
resurrected = failed_run.resurrect(
197
memory=4096, # Increase memory
198
timeout=7200 # Increase timeout
199
)
200
201
print(f"Resurrected run: {resurrected['id']}")
202
```
203
204
### Real-time Monitoring
205
206
```python
207
import time
208
from datetime import datetime
209
210
# Real-time log monitoring
211
run_client = client.run('active-run-id')
212
213
# Stream logs in real-time
214
with run_client.get_streamed_log() as log_stream:
215
for log_line in log_stream:
216
timestamp = datetime.now().strftime('%H:%M:%S')
217
print(f"[{timestamp}] {log_line.strip()}")
218
219
# Break on certain conditions
220
if 'ERROR' in log_line:
221
print("Error detected, investigating...")
222
break
223
224
# Monitor status messages
225
with run_client.get_status_message_watcher() as status_watcher:
226
for status_update in status_watcher:
227
print(f"Status: {status_update['status']}")
228
print(f"Message: {status_update.get('statusMessage', 'No message')}")
229
230
if status_update['status'] in ['SUCCEEDED', 'FAILED', 'ABORTED']:
231
break
232
```
233
234
### Data Access and Processing
235
236
```python
237
# Access run's default storage
238
run_client = client.run('completed-run-id')
239
240
# Get results from default dataset
241
dataset = run_client.dataset()
242
items = dataset.list_items()
243
244
print(f"Run produced {items.count} items")
245
246
# Process results
247
for item in items.items:
248
# Process each scraped item
249
process_scraped_data(item)
250
251
# Access metadata from key-value store
252
kvs = run_client.key_value_store()
253
input_data = kvs.get_record('INPUT')
254
output_data = kvs.get_record('OUTPUT')
255
256
print(f"Input: {input_data}")
257
print(f"Output: {output_data}")
258
259
# Get screenshot if available
260
screenshot = kvs.get_record_as_bytes('SCREENSHOT')
261
if screenshot:
262
with open('run_screenshot.png', 'wb') as f:
263
f.write(screenshot)
264
```
265
266
### Run Analytics and Billing
267
268
```python
269
# Monitor multiple runs
270
runs = client.runs().list(limit=100)
271
272
total_compute_units = 0
273
total_data_transfer = 0
274
275
for run in runs.items:
276
run_client = client.run(run['id'])
277
run_details = run_client.get()
278
279
if run_details and run_details.get('stats'):
280
stats = run_details['stats']
281
total_compute_units += stats.get('computeUnits', 0)
282
total_data_transfer += stats.get('dataTransfer', 0)
283
284
print(f"Run {run['id']}: {stats.get('computeUnits', 0)} CU")
285
286
print(f"Total compute units: {total_compute_units}")
287
print(f"Total data transfer: {total_data_transfer} MB")
288
289
# Charge for pay-per-event run
290
pay_per_event_run = client.run('pay-per-event-run-id')
291
292
# Charge for custom events
293
pay_per_event_run.charge('api_call', count=150)
294
pay_per_event_run.charge('data_extraction', items=500)
295
pay_per_event_run.charge('image_processing', images=25)
296
```
297
298
### Error Handling and Recovery
299
300
```python
301
# Graceful run management with error handling
302
def manage_run_lifecycle(actor_id, run_input):
303
client = ApifyClient('your-api-token')
304
305
try:
306
# Start run
307
run = client.actor(actor_id).start(run_input=run_input)
308
run_client = client.run(run['id'])
309
310
# Monitor with timeout
311
result = run_client.wait_for_finish(wait_secs=1800) # 30 minutes
312
313
if not result:
314
# Timeout reached, abort gracefully
315
print("Run timeout, aborting...")
316
run_client.abort(gracefully=True)
317
return None
318
319
if result['status'] == 'FAILED':
320
# Try resurrection with more resources
321
print("Run failed, attempting resurrection...")
322
resurrected = run_client.resurrect(
323
memory=8192,
324
timeout=3600
325
)
326
return resurrected
327
328
return result
329
330
except Exception as e:
331
print(f"Run management error: {e}")
332
# Clean up if needed
333
try:
334
run_client.abort()
335
except:
336
pass
337
return None
338
339
# Use the function
340
result = manage_run_lifecycle('my-actor', {'url': 'https://example.com'})
341
if result:
342
print(f"Run completed successfully: {result['id']}")
343
```