WC_Product_Data_Store_CPT::obtain_lock_on_sku_for_concurrent_requests()privateWC 1.0

Method to obtain DB lock on SKU to make sure we only create product with unique SKU for concurrent requests.

We are doing so by inserting a row in the wc_product_meta_lookup table upfront with the SKU of the product we are trying to insert.

If the SKU is already present in the table, it means that another request is processing the same SKU and we should not proceed with the insert.

Using $wpdb->options as it always has some data, if we select from a table that does not have any data, then our query will always return null set and the where subquery won't be fired, effectively bypassing any lock.

Метод класса: WC_Product_Data_Store_CPT{}

Хуки из метода

Возвращает

true|false. True if lock is obtained (unique SKU), false otherwise.

Использование

// private - только в коде основоного (родительского) класса
$result = $this->obtain_lock_on_sku_for_concurrent_requests( $product );
$product(WC_Product) (обязательный)
Product object.

Код WC_Product_Data_Store_CPT::obtain_lock_on_sku_for_concurrent_requests() WC 9.4.2

private function obtain_lock_on_sku_for_concurrent_requests( $product ) {
	global $wpdb;
	$product_id = $product->get_id();
	$sku        = $product->get_sku();

	$query = $wpdb->prepare(
		"INSERT INTO $wpdb->wc_product_meta_lookup (product_id, sku)
		SELECT %d, %s FROM $wpdb->options
		WHERE NOT EXISTS (
			SELECT * FROM $wpdb->wc_product_meta_lookup WHERE sku = %s LIMIT 1
		) LIMIT 1;",
		$product_id,
		$sku,
		$sku
	);

	/**
	 * Filter to bail early on the SKU lock query.
	 *
	 * @since 9.3.0
	 *
	 * @param bool|null  $locked  Set to a boolean value to short-circuit the SKU lock query.
	 * @param WC_Product $product The product being created.
	 */
	$locked = apply_filters( 'wc_product_pre_lock_on_sku', null, $product );
	if ( ! is_null( $locked ) ) {
		return boolval( $locked );
	}

	// The insert query can potentially result in a deadlock if there is high concurrency
	// when trying to insert products, which will result in a false negative for SKU lock
	// and incorrectly products not being created.
	// To mitigate this, we will retry the query 3 times before giving up.
	for ( $attempts = 0; $attempts < 3; $attempts++ ) {
		if ( $attempts > 1 ) {
			usleep( 10000 );
		}

		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
		$result = $wpdb->query( $query );
		if ( false !== $result ) {
			break;
		}
	}

	if ( false === $result ) {
		wc_get_logger()->warning(
			sprintf(
				'Failed to obtain SKU lock for product: ID "%d" with SKU "%s" after %d attempts.',
				$product_id,
				$sku,
				$attempts,
			),
			array(
				'error' => $wpdb->last_error,
			)
		);
	}

	return (bool) $result;
}