Skip to content

Commit 50cc93a

Browse files
authored
Add configurable retry and batch pause constants (#2360)
1 parent 584ccf5 commit 50cc93a

File tree

3 files changed

+91
-14
lines changed

3 files changed

+91
-14
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: added
3+
4+
Added new configuration options to better manage traffic spikes when federating posts, allowing finer control over retry limits, delays, and batch pauses.

includes/class-dispatcher.php

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,76 @@
1919
* @see https://www.w3.org/TR/activitypub/
2020
*/
2121
class Dispatcher {
22-
2322
/**
2423
* Batch size.
2524
*
25+
* @deprecated unreleased {@see Activitypub\Dispatcher::get_batch_size()}
26+
*
2627
* @var int
2728
*/
2829
public static $batch_size = ACTIVITYPUB_OUTBOX_PROCESSING_BATCH_SIZE;
2930

3031
/**
31-
* Error codes that qualify for a retry.
32+
* Get the batch size for processing outbox items.
33+
*
34+
* @return int The batch size.
35+
*/
36+
public static function get_batch_size() {
37+
/**
38+
* Filters the batch size for processing outbox items.
39+
*
40+
* @param int $batch_size The batch size. Default ACTIVITYPUB_OUTBOX_PROCESSING_BATCH_SIZE.
41+
*/
42+
return apply_filters( 'activitypub_dispatcher_batch_size', ACTIVITYPUB_OUTBOX_PROCESSING_BATCH_SIZE );
43+
}
44+
45+
/**
46+
* Get the maximum number of retry attempts.
47+
*
48+
* @return int The maximum number of retry attempts.
49+
*/
50+
public static function get_retry_max_attempts() {
51+
/**
52+
* Filters the maximum number of retry attempts.
53+
*
54+
* @param int $retry_max_attempts The maximum number of retry attempts. Default ACTIVITYPUB_OUTBOX_RETRY_MAX_ATTEMPTS.
55+
*/
56+
return apply_filters( 'activitypub_dispatcher_retry_max_attempts', 3 );
57+
}
58+
59+
/**
60+
* Get the retry delay unit (in seconds).
61+
*
62+
* Used to calculate exponential backoff: time() + (attempt * attempt * retry_delay_unit).
63+
*
64+
* @return int The retry delay unit in seconds.
65+
*/
66+
public static function get_retry_delay() {
67+
/**
68+
* Filters the retry delay unit (in seconds).
69+
*
70+
* Used to calculate exponential backoff: time() + (attempt * attempt * retry_delay_unit).
71+
*
72+
* @param int $retry_delay_unit The retry delay unit in seconds. Default ACTIVITYPUB_OUTBOX_RETRY_DELAY_UNIT.
73+
*/
74+
return apply_filters( 'activitypub_dispatcher_retry_delay', HOUR_IN_SECONDS );
75+
}
76+
77+
/**
78+
* Get the error codes that qualify for a retry.
3279
*
3380
* @see https://github.com/tfredrich/RestApiTutorial.com/blob/fd08b0f67f07450521d143b123cd6e1846cb2e3b/content/advanced/responses/retries.md
34-
* @var int[]
81+
*
82+
* @return int[] The error codes.
3583
*/
36-
public static $retry_error_codes = array( 408, 429, 500, 502, 503, 504 );
84+
public static function get_retry_error_codes() {
85+
/**
86+
* Filters the error codes that qualify for a retry.
87+
*
88+
* @param int[] $retry_error_codes The error codes. Default array( 408, 429, 500, 502, 503, 504 ).
89+
*/
90+
return apply_filters( 'activitypub_dispatcher_retry_error_codes', array( 408, 429, 500, 502, 503, 504 ) );
91+
}
3792

3893
/**
3994
* Initialize the class, registering WordPress hooks.
@@ -82,7 +137,7 @@ public static function process_outbox( $id ) {
82137
\do_action(
83138
'activitypub_send_activity',
84139
$outbox_item->ID,
85-
self::$batch_size,
140+
self::get_batch_size(),
86141
\get_post_meta( $outbox_item->ID, '_activitypub_outbox_offset', true ) ?: 0 // phpcs:ignore
87142
);
88143
} else {
@@ -95,13 +150,17 @@ public static function process_outbox( $id ) {
95150
/**
96151
* Asynchronously runs batch processing routines.
97152
*
98-
* @param int $outbox_item_id The Outbox item ID.
99-
* @param int $batch_size Optional. The batch size. Default ACTIVITYPUB_OUTBOX_PROCESSING_BATCH_SIZE.
100-
* @param int $offset Optional. The offset. Default 0.
153+
* @param int $outbox_item_id The Outbox item ID.
154+
* @param int|null $batch_size Optional. The batch size. Default null (uses filtered batch size).
155+
* @param int $offset Optional. The offset. Default 0.
101156
*
102157
* @return array|void The next batch of followers to process, or void if done.
103158
*/
104159
public static function send_to_followers( $outbox_item_id, $batch_size = ACTIVITYPUB_OUTBOX_PROCESSING_BATCH_SIZE, $offset = 0 ) {
160+
if ( null === $batch_size ) {
161+
$batch_size = self::get_batch_size();
162+
}
163+
105164
$outbox_item = \get_post( $outbox_item_id );
106165
$json = Outbox::get_activity( $outbox_item_id )->to_json();
107166
$inboxes = Followers::get_inboxes_for_activity( $json, $outbox_item->post_author, $batch_size, $offset );
@@ -167,7 +226,7 @@ public static function retry_send_to_followers( $transient_key, $outbox_item_id,
167226
$retries = self::send_to_inboxes( $inboxes, $outbox_item_id );
168227

169228
// Retry failed inboxes.
170-
if ( ++$attempt < 3 && ! empty( $retries ) ) {
229+
if ( ++$attempt < self::get_retry_max_attempts() && ! empty( $retries ) ) {
171230
self::schedule_retry( $retries, $outbox_item_id, $attempt );
172231
}
173232
}
@@ -196,7 +255,7 @@ private static function send_to_inboxes( $inboxes, $outbox_item_id ) {
196255
foreach ( $inboxes as $inbox ) {
197256
$result = safe_remote_post( $inbox, $json, $outbox_item->post_author );
198257

199-
if ( is_wp_error( $result ) && in_array( $result->get_error_code(), self::$retry_error_codes, true ) ) {
258+
if ( is_wp_error( $result ) && in_array( $result->get_error_code(), self::get_retry_error_codes(), true ) ) {
200259
$retries[] = $inbox;
201260
}
202261

@@ -227,7 +286,7 @@ private static function schedule_retry( $retries, $outbox_item_id, $attempt = 1
227286
\set_transient( $transient_key, $retries, WEEK_IN_SECONDS );
228287

229288
\wp_schedule_single_event(
230-
\time() + ( $attempt * $attempt * HOUR_IN_SECONDS ),
289+
\time() + ( $attempt * $attempt * self::get_retry_delay() ),
231290
'activitypub_retry_activity',
232291
array( $transient_key, $outbox_item_id, $attempt )
233292
);

includes/class-scheduler.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,20 @@ class Scheduler {
3232
*/
3333
private static $batch_callbacks = array();
3434

35+
/**
36+
* Get the pause between async batches (in seconds).
37+
*
38+
* @return int The pause in seconds.
39+
*/
40+
public static function get_retry_delay() {
41+
/**
42+
* Filters the pause between async batches (in seconds).
43+
*
44+
* @param int $async_batch_pause The pause in seconds. Default 30.
45+
*/
46+
return apply_filters( 'activitypub_scheduler_async_batch_pause', 30 );
47+
}
48+
3549
/**
3650
* Initialize the class, registering WordPress hooks.
3751
*/
@@ -140,7 +154,7 @@ public static function deregister_schedules() {
140154
public static function unschedule_events_for_item( $outbox_item_id ) {
141155
$event_args = array(
142156
$outbox_item_id,
143-
Dispatcher::$batch_size,
157+
Dispatcher::get_batch_size(),
144158
\get_post_meta( $outbox_item_id, '_activitypub_outbox_offset', true ) ?: 0, // phpcs:ignore
145159
);
146160

@@ -276,7 +290,7 @@ public static function reprocess_outbox() {
276290
foreach ( $ids as $id ) {
277291
// Bail if there is a pending batch.
278292
$offset = \get_post_meta( $id, '_activitypub_outbox_offset', true ) ?: 0; // phpcs:ignore
279-
if ( \wp_next_scheduled( 'activitypub_send_activity', array( $id, ACTIVITYPUB_OUTBOX_PROCESSING_BATCH_SIZE, $offset ) ) ) {
293+
if ( \wp_next_scheduled( 'activitypub_send_activity', array( $id, Dispatcher::get_batch_size(), $offset ) ) ) {
280294
return;
281295
}
282296

@@ -424,7 +438,7 @@ public static function async_batch() {
424438

425439
if ( ! empty( $next ) ) {
426440
// Schedule the next run, adding the result to the arguments.
427-
\wp_schedule_single_event( \time() + 30, \current_action(), \array_values( $next ) );
441+
\wp_schedule_single_event( \time() + self::get_retry_delay(), \current_action(), \array_values( $next ) );
428442
}
429443
}
430444

0 commit comments

Comments
 (0)