Automattic\WooCommerce\Internal\Admin\Schedulers
OrdersScheduler::process_pending_batch
Process pending orders in batch.
This method queries for orders updated since the last cursor position (compound cursor: date + ID) and imports them into the analytics tables.
Method of the class: OrdersScheduler{}
No Hooks.
Returns
null. Nothing (null).
Usage
$result = OrdersScheduler::process_pending_batch( $cursor_date, $cursor_id );
- $cursor_date(string|null)
- Cursor date in
'Y-m-d H:i:s'format. Orders after this date will be processed.
Default:null - $cursor_id(int|null)
- Cursor order ID. Combined with
$cursor_dateto form compound cursor.
Default:null
OrdersScheduler::process_pending_batch() OrdersScheduler::process pending batch code WC 11.0.1
public static function process_pending_batch( $cursor_date = null, $cursor_id = null ) {
$logger = wc_get_logger();
$context = array( 'source' => 'wc-analytics-order-import' );
if ( self::is_importing() ) {
// No need to process if an import is already in progress.
$logger->info( 'Import is already in progress, skipping batch import.', $context );
return;
}
// Load cursor position from options if not provided.
// If the cursor date is not provided, use the last 24 hours as the default since `action_scheduler_ensure_recurring_actions` runs daily so 24 hours is enough.
$default_cursor_date = gmdate( 'Y-m-d H:i:s', strtotime( '-24 hours' ) );
$cursor_date = $cursor_date ?? get_option( self::LAST_PROCESSED_ORDER_DATE_OPTION, $default_cursor_date );
$cursor_id = $cursor_id ?? (int) get_option( self::LAST_PROCESSED_ORDER_ID_OPTION, 0 );
// Validate cursor date.
if ( ! $cursor_date || ! strtotime( $cursor_date ) ) {
$logger->error( 'Invalid cursor date: ' . $cursor_date, $context );
$cursor_date = $default_cursor_date;
}
$batch_size = self::get_batch_size( self::PROCESS_PENDING_ORDERS_BATCH_ACTION );
$logger->info(
sprintf( 'Starting batch import. Cursor: %s (ID: %d), batch size: %d', $cursor_date, $cursor_id, $batch_size ),
$context
);
$start_time = microtime( true );
// Get orders updated since the cursor position.
$orders = self::get_orders_since( $cursor_date, $cursor_id, $batch_size );
if ( empty( $orders ) ) {
$logger->info( 'No orders to process', $context );
// Update the cursor position to the start time of the batch so that the next batch will start from that point.
update_option( self::LAST_PROCESSED_ORDER_DATE_OPTION, gmdate( 'Y-m-d H:i:s', (int) $start_time ), false );
update_option( self::LAST_PROCESSED_ORDER_ID_OPTION, 0, false );
return;
}
$orders_count = count( $orders );
$processed_count = 0;
foreach ( $orders as $order ) {
try {
self::import( $order->id );
++$processed_count;
// Advance cursor after each successful import. Since orders are sorted by
// date ASC, id ASC, we can simply overwrite with the current order's values.
$cursor_date = $order->date_updated_gmt;
$cursor_id = $order->id;
} catch ( \Throwable $e ) {
// Log the failure and advance the cursor past the failing order so that
// it is skipped on the next run rather than blocking the entire pipeline.
static::log_import_error( $order->id, $e, $context );
static::record_failed_order_import( $order->id );
$cursor_date = $order->date_updated_gmt;
$cursor_id = $order->id;
}
}
// Save the updated cursor position.
update_option( self::LAST_PROCESSED_ORDER_DATE_OPTION, $cursor_date, false );
update_option( self::LAST_PROCESSED_ORDER_ID_OPTION, $cursor_id, false );
$elapsed_time = microtime( true ) - $start_time;
$logger->info(
sprintf(
'Batch import completed. Processed: %d/%d orders in %.2f seconds. Cursor: %s (ID: %d)',
$processed_count,
$orders_count,
$elapsed_time,
$cursor_date,
$cursor_id
),
$context
);
// If we fetched a full batch, there might be more orders to process.
// Use the fetched count rather than successful count so that skipped
// failing orders do not suppress scheduling of the next batch.
if ( $orders_count === $batch_size ) {
$logger->info( 'Full batch processed, scheduling next batch', $context );
self::schedule_action(
'process_pending_batch',
array( $cursor_date, $cursor_id )
);
}
}