fix(2meet-data-optimizer): close the gaps in deferred table upgrades found in the 2.3 review
- a deferred group whose stored definition matches again (reverted, or upgraded by another process) goes through end_deferral() instead of staying native for good (review F2) - the tmdo_ensure_group_indexes handler upgrades deferred tables itself: with ALTERNATE_WP_CRON the event runs after init in a visitor's request (F3) - a new group table created while the entity writes to flat stays native until the drift fix has filled it from native meta; an opted-in HivePress attribute no longer routes searches to an empty table (F6) - direct_read() returns native values for a deferred group (F10); the Hook Bus ignores deferral in aeav_only, as the router does - dbDelta failures: the definition is recorded only when every column exists (schema.upgrade_failed otherwise), so a failed ALTER is retried and the group is not released onto missing columns - test bootstrap: get_metadata() reads the real meta table - Verify: SchemaUpgradeDeferralTest (6), Hook Bus direct read, integration 587, unit 703, phpcs, stan - PR: PR-114 (2.3 acceptance correctness review)
This commit is contained in:
@@ -88,6 +88,8 @@ final class TMDO_Schema_Manager {
|
||||
$stored_hash = self::get_stored_schema_hash( $entity_type, $group_name );
|
||||
|
||||
if ( $stored_hash === $schema_hash ) {
|
||||
// Upgraded meanwhile, or the definition went back to the stored one.
|
||||
self::end_deferral( $entity_type, $group_name );
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -102,7 +104,16 @@ final class TMDO_Schema_Manager {
|
||||
return false;
|
||||
}
|
||||
if ( ! self::table_exists( $table ) ) {
|
||||
return self::write_table( $entity_type, $group_name, $field_definitions, $adapter, $table, $schema_hash );
|
||||
if ( ! self::write_table( $entity_type, $group_name, $field_definitions, $adapter, $table, $schema_hash ) ) {
|
||||
return false;
|
||||
}
|
||||
// A new table starts empty while native meta may already hold the
|
||||
// group's values: native until the drift fix has filled it in.
|
||||
if ( TMDO_Mode_Manager::writes_to_flat( $entity_type ) && TMDO_Mode_Manager::MODE_AEAV_ONLY !== TMDO_Mode_Manager::get( $entity_type ) ) {
|
||||
self::record_deferred( "{$entity_type}/{$group_name}" );
|
||||
self::end_deferral( $entity_type, $group_name );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// An existing table is altered by one process at a time (anonymous
|
||||
@@ -134,6 +145,7 @@ final class TMDO_Schema_Manager {
|
||||
*/
|
||||
private static function write_table( string $entity_type, string $group_name, array $field_definitions, TMDO_Entity_Adapter_Interface $adapter, string $table, string $schema_hash ): bool {
|
||||
global $wpdb;
|
||||
$existed = self::table_exists( $table );
|
||||
|
||||
$charset = $wpdb->get_charset_collate();
|
||||
$id_col = $adapter->get_entity_id_column();
|
||||
@@ -204,9 +216,27 @@ final class TMDO_Schema_Manager {
|
||||
// Bust the request-scoped table_exists cache so subsequent calls see the new table.
|
||||
self::$table_exists_cache[ $table ] = true;
|
||||
|
||||
// dbDelta reports no failure: record the definition only when every column is there.
|
||||
$columns = (array) $wpdb->get_col( "SHOW COLUMNS FROM `{$table}`" );
|
||||
$missing = array_diff( array_map( static fn( $field ) => self::sanitize_column_name( (string) $field['key'] ), $field_definitions ), $columns );
|
||||
if ( $missing ) {
|
||||
unset( self::$table_exists_cache[ $table ] );
|
||||
TMDO_Logger::warning(
|
||||
'schema.upgrade_failed',
|
||||
array(
|
||||
'table' => $table,
|
||||
'missing' => array_values( $missing ),
|
||||
'error' => $wpdb->last_error,
|
||||
)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 記錄 Schema 版本與定義
|
||||
self::store_schema_metadata( $entity_type, $group_name, $schema_hash, $field_definitions );
|
||||
self::end_deferral( $entity_type, $group_name );
|
||||
if ( $existed ) {
|
||||
self::end_deferral( $entity_type, $group_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Action: 表建立/升級完成
|
||||
@@ -294,11 +324,20 @@ final class TMDO_Schema_Manager {
|
||||
}
|
||||
|
||||
/**
|
||||
* INDEXES_HOOK handler: add the indexes a visitor request could not.
|
||||
* INDEXES_HOOK handler: the table upgrades and indexes a web request left.
|
||||
* The cron request's init usually did them already; not with
|
||||
* ALTERNATE_WP_CRON, where the event runs inside a visitor's request.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function ensure_pending_indexes(): void {
|
||||
foreach ( array_keys( (array) get_option( self::DEFERRED_OPTION, array() ) ) as $id ) {
|
||||
list( $entity_type, $group_name ) = array_pad( explode( '/', (string) $id, 2 ), 2, '' );
|
||||
$fields = TMDO_Entity_Registry::get_group_fields( $entity_type, $group_name );
|
||||
if ( $fields ) {
|
||||
self::create_or_upgrade_table( $entity_type, $group_name, $fields );
|
||||
}
|
||||
}
|
||||
foreach ( self::pending_indexes() as $id ) {
|
||||
list( $entity_type, $group_name ) = explode( '/', $id, 2 );
|
||||
self::ensure_group_indexes( $entity_type, $group_name );
|
||||
@@ -341,15 +380,25 @@ final class TMDO_Schema_Manager {
|
||||
* @return void
|
||||
*/
|
||||
private static function defer_upgrade( string $id ): void {
|
||||
self::record_deferred( $id );
|
||||
if ( ! wp_next_scheduled( self::INDEXES_HOOK ) ) {
|
||||
wp_schedule_single_event( time(), self::INDEXES_HOOK ); // Its request's init runs the upgrade.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a group as deferred (native until resynced()).
|
||||
*
|
||||
* @param string $id "entity/group".
|
||||
* @return void
|
||||
*/
|
||||
private static function record_deferred( string $id ): void {
|
||||
$deferred = get_option( self::DEFERRED_OPTION, array() );
|
||||
$deferred = is_array( $deferred ) ? $deferred : array();
|
||||
if ( ! isset( $deferred[ $id ] ) ) {
|
||||
$deferred[ $id ] = time();
|
||||
update_option( self::DEFERRED_OPTION, $deferred, true );
|
||||
}
|
||||
if ( ! wp_next_scheduled( self::INDEXES_HOOK ) ) {
|
||||
wp_schedule_single_event( time(), self::INDEXES_HOOK ); // Its request's init runs the upgrade.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user