Commit e9ef0196e33 for woocommerce
commit e9ef0196e335a5a0c27e27d3093f1503f217f123
Author: Boro Sitnikovski <buritomath@gmail.com>
Date: Wed Mar 18 15:59:40 2026 +0100
Add woocommerce_draft_order_batch_size filter to DraftOrders cleanup (#63740)
* Add woocommerce_draft_order_batch_size filter to DraftOrders cleanup
Make the batch size in delete_expired_draft_orders() filterable via
`woocommerce_draft_order_batch_size` (default: 20) so high-traffic sites
can tune cleanup throughput without patching core.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* Changelog
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Fix hard-coded batch size check in assert_order_results and add regression test
The guard in assert_order_results compared against literal 20 instead of
the $expected_batch_size parameter, causing a false exception when the
woocommerce_draft_order_batch_size filter returned a value greater than 20.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* Fix phpcs lint errors in new test method
- Add missing doc comment
- Add space after function keyword in closures
- Expand inline closure to multi-line
- Remove spaces around string array keys
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* Rename filter to woocommerce_delete_expired_draft_orders_batch_size
Makes it clear the filter scopes to the expired draft order deletion
specifically, not draft order handling in general.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* Improve filter docblock to clarify throughput use case
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* Fix test to create distinct WC_Order instances per batch
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
diff --git a/plugins/woocommerce/changelog/add-draft-orders-batch-size-filter b/plugins/woocommerce/changelog/add-draft-orders-batch-size-filter
new file mode 100644
index 00000000000..5fdb0cf45ac
--- /dev/null
+++ b/plugins/woocommerce/changelog/add-draft-orders-batch-size-filter
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Add woocommerce_delete_expired_draft_orders_batch_size filter to make draft order cleanup batch size configurable
diff --git a/plugins/woocommerce/src/Blocks/Domain/Services/DraftOrders.php b/plugins/woocommerce/src/Blocks/Domain/Services/DraftOrders.php
index 422b09742a0..a8b05145455 100644
--- a/plugins/woocommerce/src/Blocks/Domain/Services/DraftOrders.php
+++ b/plugins/woocommerce/src/Blocks/Domain/Services/DraftOrders.php
@@ -169,15 +169,25 @@ class DraftOrders {
}
/**
- * Delete draft orders older than a day in batches of 20.
+ * Delete draft orders older than a day in configurable batches (default: 20).
*
- * Ran on a daily cron schedule.
+ * Ran on a daily cron schedule. Batch size is filterable via
+ * `woocommerce_delete_expired_draft_orders_batch_size`.
*
* @internal
*/
public function delete_expired_draft_orders() {
- $count = 0;
- $batch_size = 20;
+ $count = 0;
+ /**
+ * Filters the number of draft orders deleted per batch during cleanup.
+ *
+ * Increasing this value can help improve deletion throughput for high-volume or busy stores
+ * when the cleanup task cannot keep up with the draft orders backlog.
+ *
+ * @since 10.7.0
+ * @param int $batch_size Number of draft orders to delete per batch. Default 20.
+ */
+ $batch_size = max( 1, (int) apply_filters( 'woocommerce_delete_expired_draft_orders_batch_size', 20 ) );
$this->ensure_draft_status_registered();
$orders = wc_get_orders(
[
@@ -239,7 +249,7 @@ class DraftOrders {
$suffix = ' This is an indicator that something is filtering WooCommerce or WordPress queries and modifying the query parameters.';
// if count is greater than our expected batch size, then that's a problem.
- if ( count( $order_results ) > 20 ) {
+ if ( count( $order_results ) > $expected_batch_size ) {
throw new Exception( 'There are an unexpected number of results returned from the query.' . $suffix );
}
diff --git a/plugins/woocommerce/tests/php/src/Blocks/Domain/Services/DeleteDraftOrders.php b/plugins/woocommerce/tests/php/src/Blocks/Domain/Services/DeleteDraftOrders.php
index 50d0d179cb5..8534dc0c536 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/Domain/Services/DeleteDraftOrders.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/Domain/Services/DeleteDraftOrders.php
@@ -123,6 +123,37 @@ class DeleteDraftOrders extends TestCase {
$this->assertEquals( 1, (int) $wpdb->get_var( "SELECT COUNT(ID) from $wpdb->posts posts WHERE posts.post_status = 'wc-on-hold'" ) );
}
+ /**
+ * Test that a custom batch size filter allows more than the default 20 results without error.
+ */
+ public function test_custom_batch_size_filter_allows_larger_results() {
+ add_filter(
+ 'woocommerce_delete_expired_draft_orders_batch_size',
+ function () {
+ return 50;
+ }
+ );
+
+ $sample_results = function ( $results, $args ) {
+ if ( isset( $args['status'] ) && DraftOrders::DB_STATUS === $args['status'] ) {
+ $orders = array();
+ for ( $i = 0; $i < 50; $i++ ) {
+ $order = new WC_Order();
+ $order->set_status( DraftOrders::STATUS );
+ $orders[] = $order;
+ }
+ return $orders;
+ }
+ return $results;
+ };
+ $this->mock_results_for_wc_query( $sample_results );
+ $this->draft_orders_instance->delete_expired_draft_orders();
+ $this->assertNull( $this->caught_exception, 'No exception should be thrown when batch size filter allows more results.' );
+ $this->unset_mock_results_for_wc_query( $sample_results );
+
+ remove_all_filters( 'woocommerce_delete_expired_draft_orders_batch_size' );
+ }
+
public function test_greater_than_batch_results_error() {
$sample_results = function( $results, $args ) {
if ( isset( $args[ 'status' ] ) && DraftOrders::DB_STATUS === $args[ 'status' ] ) {