Skip to content

Fix HPPS migrations for restricted hosting environments#7899

Merged
donnapep merged 34 commits into
trunkfrom
fix/hpps-migration-time-budget
Mar 4, 2026
Merged

Fix HPPS migrations for restricted hosting environments#7899
donnapep merged 34 commits into
trunkfrom
fix/hpps-migration-time-budget

Conversation

@donnapep

@donnapep donnapep commented Feb 24, 2026

Copy link
Copy Markdown
Member

Summary

  • Remove set_time_limit(0) from migration scheduler and replace with time-budgeted processing (default 20s, filterable)
  • Refactor Student Progress migration to process one comment at a time with streaming inserts, flushing every 50 rows
  • Reduce migration batch sizes (Student Progress: 1000→250 comments/run, Quiz: 100→50)
  • Fix cursor tracking so it only advances after rows are flushed to the database
  • Add retry logic for failed migrations (3 retries before marking as failed)
  • Remove Settings UI gate that blocked HPPS on hosts where set_time_limit is disabled
  • Fix typo: LARST_COMMENT_ID_OPTION_NAMELAST_COMMENT_ID_OPTION_NAME

Note: I'm fixing up the PHP unit test jobs in a separate PR, so you can ignore any jobs that didn't complete.

Context

HPPS (High-Performance Progress Storage) Stage 4 — Phase 1. Migrations previously required set_time_limit(0) which fails on WP.com Atomic and restricted hosting environments. This PR makes migrations work within normal PHP execution limits by introducing a time budget that allows migrations to stop mid-batch and safely resume on the next run.

New filters

  • sensei_migration_time_budget (float, default 20.0) — seconds per migration run
  • sensei_migration_max_retries (int, default 3) — retry attempts before failing
  • sensei_migration_student_progress_read_batch_size (int, default 250) — comments fetched per run
  • sensei_migration_student_progress_insert_batch_size (int, default 50) — comments inserted per run
  • sensei_migration_quiz_batch_size (int, default 50) — quiz submissions fetched per run

Test plan

  • Verify HPPS toggle is enabled in settings on an Atomic site
  • Test migration completes successfully with reduced batch sizes

donnapep and others added 9 commits February 24, 2026 08:22
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 24, 2026 14:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes HPPS (High-Performance Progress Storage) migrations compatible with restricted hosting environments (like WP.com Atomic) where set_time_limit() is disabled. The changes implement time-budgeted processing, reduce batch sizes to work within normal PHP execution limits, and add retry logic for transient failures.

Changes:

  • Implemented time-budgeted migration processing with 80% threshold and default 20-second budget
  • Reduced migration batch sizes: Student Progress from 1000 to 250 rows/run (50×5), Quiz from 100 to 50 submissions/run
  • Added retry logic with default 3 attempts before marking migrations as failed
  • Removed Settings UI restrictions requiring set_time_limit() support
  • Fixed typo in constant name with backward-compatible alias

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
includes/internal/migration/class-migration-abstract.php Added time budget tracking with set_time_budget() and is_time_exceeded() methods using 80% threshold
includes/internal/migration/class-migration-job.php Added set_time_budget() method to delegate budget setting to underlying migration
includes/internal/migration/class-migration-job-scheduler.php Removed set_time_limit(0), added retry logic with configurable max retries, applies time budget filter to jobs
includes/internal/migration/migrations/class-student-progress-migration.php Reduced batch size/count defaults, added filterable batch configuration, checks time budget in insert_with_batches(), fixed typo with backward compatibility
includes/internal/migration/migrations/class-quiz-migration.php Reduced batch size default, added filterable batch size, checks time budget in main processing loop
includes/class-sensei-settings.php Removed set_time_limit() capability check that previously disabled HPPS feature on restricted hosts
tests/unit-tests/internal/migration/test-class-migration-abstract.php New test file covering time budget functionality
tests/unit-tests/internal/migration/test-class-migration-job.php Added test for time budget delegation
tests/unit-tests/internal/migration/test-class-migration-job-scheduler.php Added tests for time budget application, retry logic, and retry count cleanup
tests/unit-tests/internal/migration/migrations/test-class-student-progress-migration.php Added tests for reduced batch sizes, filterable batch configuration, and time budget behavior
tests/unit-tests/internal/migration/migrations/test-class-quiz-migration.php Added tests for reduced batch size, filterable batch size, and time budget behavior
.gitignore Added .worktrees for git worktree support
Comments suppressed due to low confidence (1)

includes/internal/migration/migrations/class-quiz-migration.php:101

  • The return value documentation states "The number of quiz submissions migrated" but line 101 returns count( $comments ) which is the number of comments processed, not necessarily the number of quiz submissions successfully migrated. When a comment has no associated quiz (line 81-84), it's counted in the returned value even though no submission was migrated. Line 94 returns $processed which has the same issue.

Consider either updating the documentation to say "The number of comments processed" or tracking and returning the actual count of successfully migrated submissions.

	 * @return int The number of quiz submissions migrated.
	 */
	public function run( bool $dry_run = true ) {
		$this->dry_run = $dry_run;

		$comments = $this->get_comments();
		if ( ! $comments ) {
			return 0;
		}

		$quiz_data = $this->get_quiz_data( $comments );
		$processed = 0;
		foreach ( $comments as $comment ) {
			$submission_id = $this->insert_quiz_submission( $comment, $quiz_data );
			if ( ! $submission_id ) {
				++$processed;
				continue;
			}

			$answer_ids = $this->insert_quiz_answers( $comment, $quiz_data, $submission_id );
			$this->insert_quiz_grades( $comment, $quiz_data, $answer_ids );

			++$processed;

			// Update cursor and stop if time budget exceeded.
			if ( $this->is_time_exceeded() && $processed < count( $comments ) ) {
				update_option( self::LAST_COMMENT_ID_OPTION_NAME, $comment->comment_ID );
				return $processed;
			}
		}

		$last_comment_id = end( $comments )->comment_ID;
		update_option( self::LAST_COMMENT_ID_OPTION_NAME, $last_comment_id );

		return count( $comments );

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread includes/internal/migration/migrations/class-student-progress-migration.php Outdated
@donnapep donnapep self-assigned this Feb 24, 2026
@donnapep donnapep added this to the 4.25.3 milestone Feb 24, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@donnapep donnapep added the Hooks This change adds or modifies one or more hooks. label Feb 24, 2026
donnapep and others added 2 commits February 24, 2026 10:42
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When insert_with_batches() exits early due to time budget, the cursor
was still set to the last fetched comment ID, skipping rows that were
never inserted. Now the cursor only advances if the time budget has
not been exceeded. INSERT IGNORE handles duplicates on re-run safely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Copilot AI commented Feb 24, 2026

Copy link
Copy Markdown

@donnapep I've opened a new pull request, #7900, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

donnapep and others added 7 commits February 24, 2026 11:02
Add missing @SInCE, @return, and @var tags to new code introduced in
this PR. Fix incorrect doc block description on Quiz_Migration constant.
Add doc blocks to previously undocumented status constants.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove tests for batch size defaults, batch size filters, and backward
compat constant alias. Add descriptive messages to time budget assertions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
donnapep and others added 3 commits February 24, 2026 15:07
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of not advancing the cursor when time is exceeded, track the
last fully processed comment ID and always advance to it. This prevents
infinite rescheduling when a batch consistently exceeds the time budget.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@donnapep donnapep force-pushed the fix/hpps-migration-time-budget branch from 6fb7fac to 0263ca8 Compare February 25, 2026 13:01
donnapep and others added 2 commits February 25, 2026 08:51
Replace the bulk prepare-then-insert approach with per-comment
processing, matching the pattern already used by Quiz_Migration.
Each comment is fully processed before the time budget is checked,
guaranteeing forward progress by construction without needing
special-case logic for stalled runs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Accumulate rows from multiple comments into a buffer and flush with a
single multi-row INSERT IGNORE when the buffer reaches insert_batch_size
(default 50). This keeps per-comment cursor tracking and time-budget
support while restoring the performance of batched inserts (~2.3x faster
than per-comment inserts at 250 users / 1200 rows).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@donnapep donnapep force-pushed the fix/hpps-migration-time-budget branch from fdc0f29 to 6b60f90 Compare February 25, 2026 14:19
donnapep and others added 5 commits February 25, 2026 09:37
Return the number of comments processed from Student_Progress_Migration::run()
instead of the number of rows inserted by INSERT IGNORE. This prevents the
migration scheduler from marking the migration as complete when a crash occurs
between the INSERT and the cursor update, causing all re-processed rows to be
duplicates (INSERT IGNORE returns 0).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The last_prepared_id and last_processed_id distinction was unnecessary
since the cursor is only ever updated after a flush.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The variable was called $rows_inserted but the migration's run() method
returns the number of comments processed, not rows inserted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove unused $trashed_post_ids variable and fix docstring that
referenced a non-existent method.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move $last_processed_id assignment inside the foreach loop body so it
is set on every iteration, eliminating the post-loop reference to the
foreach variable $progress_comment that Psalm flagged as possibly
undefined.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@donnapep donnapep requested a review from m1r0 February 25, 2026 16:04
@donnapep

Copy link
Copy Markdown
Member Author

@merkushin As we chatted, here's the first PR. 🙂

Comment thread includes/internal/migration/migrations/class-quiz-migration.php Outdated
Comment thread includes/internal/migration/migrations/class-student-progress-migration.php Outdated
Comment thread includes/internal/migration/migrations/class-student-progress-migration.php Outdated
*/
$this->batch_size = (int) apply_filters( 'sensei_migration_student_progress_batch_size', $batch_size );

$this->insert_batch_size = $insert_batch_size;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding another filter here might be useful too.
Or maybe they (read batch size and insert batch size) could be combined into a single filter?

@donnapep donnapep Mar 2, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The insert batch size is an internal implementation detail — it just controls how many rows are buffered before flushing a multi-row INSERT. The default of 50 is reasonable and there's no realistic scenario where a site admin would need to change it independently.

If someone needs to reduce memory pressure, they'd lower the read batch size instead. That doesn't change the insert buffer threshold, but it reduces the total number of rows flowing through the pipeline — e.g. reading 50 comments instead of 250 means fewer rows are produced overall, so each flush is naturally smaller even though the buffer size stays at 50.

I don't think adding a filter or combining the two adds value here — it would just expose plumbing that nobody should need to touch.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A very Claude-esque answer, but one that makes sense to me. WDYT?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, both values come from the open interface of the class, so it's a bit slick to say one is part of the internal logic :)

If someone wants to have full control over the process, having the batch size filter would be really good. It might help to address this restriction on shared hostings: https://dev.mysql.com/doc/refman/8.4/en/packet-too-large.html

At the same time, we had none of them before 😅

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in cece81a.

@donnapep donnapep requested a review from mdawaffe March 2, 2026 20:38
donnapep and others added 3 commits March 2, 2026 15:59
- Rename $batch_size to $read_batch_size in Student_Progress_Migration
- Use past tense in test method names for duplicate-row conditions
- Replace @SInCE 4.26.0 with $$next-version$$ placeholder

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@mdawaffe mdawaffe left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach looks nice to me. I don't know how to test everything :) but I'll look into that more tomorrow.

Add an apply_filters call for insert_batch_size so site admins can
control the multi-row INSERT size (e.g. on hosts with a small
max_allowed_packet). Also rename the read filter from
sensei_migration_student_progress_batch_size to
sensei_migration_student_progress_read_batch_size for consistency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@donnapep donnapep merged commit 1f6aa58 into trunk Mar 4, 2026
23 checks passed
@donnapep donnapep deleted the fix/hpps-migration-time-budget branch March 4, 2026 11:37
@donnapep donnapep added the HPPS label Mar 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Hooks This change adds or modifies one or more hooks. HPPS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants