Plugin Directory

Changeset 3469618


Ignore:
Timestamp:
02/25/2026 04:39:00 PM (4 weeks ago)
Author:
intufind
Message:

Release intufind v1.2.3

Location:
intufind
Files:
34 added
4 edited

Legend:

Unmodified
Added
Removed
  • intufind/trunk/includes/class-intufind-api.php

    r3462954 r3469618  
    8888    }
    8989
     90    const MAX_RETRIES = 2;
     91    const RETRY_BASE_DELAY = 1;
     92
    9093    /**
    9194     * Make an authenticated API request.
     95     *
     96     * Rate-limit retries are OFF by default. Callers that send idempotent
     97     * payloads (bulk upserts, deletes) should pass retries: true explicitly.
    9298     *
    9399     * @param string $method   HTTP method (GET, POST, PUT, DELETE).
     
    95101     * @param array  $data     Request body data (for POST/PUT).
    96102     * @param array  $args     Additional wp_remote_request args.
     103     *                         Pass 'retries' => true to enable 429 retry with backoff.
    97104     * @return array|WP_Error Response data or WP_Error on failure.
    98105     */
    99106    public function request( $method, $endpoint, $data = array(), $args = array() ) {
     107        $retry_enabled = ! empty( $args['retries'] );
     108        unset( $args['retries'] );
     109
    100110        $api_key = $this->get_api_key();
    101111
     
    132142        $request_args = wp_parse_args( $args, $default_args );
    133143
    134         // Make the request.
    135         $response = wp_remote_request( $url, $request_args );
    136 
    137         // Check for WP_Error.
    138         if ( is_wp_error( $response ) ) {
    139             return $response;
    140         }
    141 
    142         // Parse response.
    143         $status_code = wp_remote_retrieve_response_code( $response );
    144         $body        = wp_remote_retrieve_body( $response );
    145         $data        = json_decode( $body, true );
    146 
    147         // Check for HTTP errors.
    148         if ( $status_code >= 400 ) {
     144        $max_attempts = $retry_enabled ? self::MAX_RETRIES : 0;
     145        $last_error   = null;
     146
     147        for ( $attempt = 0; $attempt <= $max_attempts; $attempt++ ) {
     148            if ( $attempt > 0 ) {
     149                $delay_ms = (int) ( self::RETRY_BASE_DELAY * pow( 2, $attempt - 1 ) * 1000 );
     150                $jitter   = wp_rand( 0, (int) ( $delay_ms * 0.3 ) );
     151                usleep( ( $delay_ms + $jitter ) * 1000 );
     152            }
     153
     154            $response = wp_remote_request( $url, $request_args );
     155
     156            if ( is_wp_error( $response ) ) {
     157                return $response;
     158            }
     159
     160            $status_code = wp_remote_retrieve_response_code( $response );
     161            $body        = wp_remote_retrieve_body( $response );
     162            $data        = json_decode( $body, true );
     163
     164            if ( $status_code < 400 ) {
     165                return $data;
     166            }
     167
    149168            $error_default = __( 'API request failed.', 'intufind' );
    150169            $error_message = isset( $data['error'] )
     
    152171                : $error_default;
    153172
    154             // Provide user-friendly messages for common error codes.
    155173            if ( 429 === $status_code ) {
    156                 $api_error_code = isset( $data['errorCode'] ) ? $data['errorCode'] : '';
    157                 $is_limit       = 'INDEXING_LIMIT_EXCEEDED' === $api_error_code
    158                                 || stripos( $error_message, 'limit exceeded' ) !== false;
     174                $api_error_code = isset( $data['error']['code'] ) ? $data['error']['code'] : '';
     175                $is_limit       = 'INDEXING_LIMIT_EXCEEDED' === $api_error_code;
    159176
    160177                if ( $is_limit ) {
     
    166183                }
    167184
    168                 return new WP_Error(
     185                $last_error = new WP_Error(
    169186                    'rate_limited',
    170187                    __( 'Too many requests. Please wait a moment and try again.', 'intufind' ),
    171188                    array( 'status' => $status_code )
    172189                );
     190                continue;
    173191            }
    174192
     
    176194        }
    177195
    178         return $data;
     196        return $last_error;
    179197    }
    180198
     
    384402     */
    385403    public function upsert_posts( $posts ) {
    386         return $this->request( 'POST', 'posts/bulk', array( 'entities' => $posts ) );
     404        return $this->request( 'POST', 'posts/bulk', array( 'entities' => $posts ), array( 'retries' => true ) );
    387405    }
    388406
     
    394412     */
    395413    public function delete_posts( $post_ids ) {
    396         return $this->request( 'DELETE', 'posts/bulk', array( 'ids' => $post_ids ) );
     414        return $this->request( 'DELETE', 'posts/bulk', array( 'ids' => $post_ids ), array( 'retries' => true ) );
    397415    }
    398416
     
    404422     */
    405423    public function upsert_products( $products ) {
    406         return $this->request( 'POST', 'products/bulk', array( 'entities' => $products ) );
     424        return $this->request( 'POST', 'products/bulk', array( 'entities' => $products ), array( 'retries' => true ) );
    407425    }
    408426
     
    414432     */
    415433    public function delete_products( $product_ids ) {
    416         return $this->request( 'DELETE', 'products/bulk', array( 'ids' => $product_ids ) );
     434        return $this->request( 'DELETE', 'products/bulk', array( 'ids' => $product_ids ), array( 'retries' => true ) );
    417435    }
    418436
     
    683701    public function upsert_taxonomies( $taxonomies ) {
    684702        // Taxonomy bulk upsert expects a direct array, not wrapped in an object.
    685         return $this->request( 'POST', 'taxonomies/bulk', $taxonomies );
     703        return $this->request( 'POST', 'taxonomies/bulk', $taxonomies, array( 'retries' => true ) );
    686704    }
    687705
     
    693711     */
    694712    public function delete_taxonomies( $taxonomy_ids ) {
    695         return $this->request( 'DELETE', 'taxonomies/bulk', array( 'ids' => $taxonomy_ids ) );
     713        return $this->request( 'DELETE', 'taxonomies/bulk', array( 'ids' => $taxonomy_ids ), array( 'retries' => true ) );
    696714    }
    697715
  • intufind/trunk/includes/class-intufind-sync.php

    r3462954 r3469618  
    608608        $processed_ids  = array();
    609609        $skipped_count  = 0;
     610        $limit_reached  = false;
    610611
    611612        foreach ( $items as $item ) {
     613            if ( $limit_reached ) {
     614                break;
     615            }
     616
    612617            $post_id = $item['post_id'];
    613618
     
    637642            // Process in batches.
    638643            if ( count( $documents ) >= self::BULK_BATCH_SIZE ) {
    639                 $this->send_bulk_upsert( $post_type, $documents, $processed_ids );
     644                $limit_reached = $this->send_bulk_upsert( $post_type, $documents, $processed_ids );
    640645                $documents     = array();
    641646                $processed_ids = array();
     
    643648        }
    644649
    645         // Process remaining.
    646         if ( ! empty( $documents ) ) {
     650        // Process remaining (unless limit was already hit).
     651        if ( ! empty( $documents ) && ! $limit_reached ) {
    647652            $this->send_bulk_upsert( $post_type, $documents, $processed_ids );
    648653        }
     
    658663     * @param array  $documents    Documents to upsert.
    659664     * @param array  $processed_ids Post IDs being processed.
    660      * @return void
     665     * @return bool True if indexing limit was reached.
    661666     */
    662667    private function send_bulk_upsert( $post_type, $documents, $processed_ids ) {
    663668        // Store hashes for status update.
    664669        $hashes = array();
    665         foreach ( $documents as $doc ) {
     670        foreach ( $documents as &$doc ) {
    666671            $hashes[ $doc['id'] ] = $doc['_hash'] ?? '';
    667672            unset( $doc['_hash'] );
    668673        }
     674        unset( $doc );
    669675
    670676        // Send to API.
     
    680686            $error_code    = $result->get_error_code();
    681687
    682             // Use a concise per-item message for indexing limits.
    683688            if ( 'indexing_limit_exceeded' === $error_code ) {
    684689                $error_message = __( 'Plan indexing limit reached', 'intufind' );
     
    688693                $this->status->mark_error( $post_id, $error_message );
    689694            }
    690         } else {
    691             // Check for individual failures in response.
    692             $failed = $this->extract_failed_items( $result );
    693 
    694             foreach ( $processed_ids as $post_id ) {
    695                 if ( in_array( (string) $post_id, $failed['ids'], true ) ) {
    696                     $error_msg = $this->find_failed_item_error( $failed['items'], $post_id );
    697                     $this->status->mark_error( $post_id, $error_msg );
    698                 } else {
    699                     $hash = $hashes[ (string) $post_id ] ?? '';
    700                     $this->status->mark_synced( $post_id, $hash, 'auto' );
    701                 }
    702             }
    703         }
     695
     696            return 'indexing_limit_exceeded' === $error_code;
     697        }
     698
     699        $failed      = $this->extract_failed_items( $result );
     700        $success_ids = isset( $result['data']['successfulIds'] ) ? $result['data']['successfulIds'] : array();
     701        $success_set = array_flip( array_map( 'strval', $success_ids ) );
     702        $limit_hit   = ! empty( $result['data']['indexingLimitReached'] );
     703
     704        foreach ( $processed_ids as $post_id ) {
     705            if ( in_array( (string) $post_id, $failed['ids'], true ) ) {
     706                $error_msg = $this->find_failed_item_error( $failed['items'], $post_id );
     707                $this->status->mark_error( $post_id, $error_msg );
     708            } elseif ( isset( $success_set[ (string) $post_id ] ) ) {
     709                $hash = $hashes[ (string) $post_id ] ?? '';
     710                $this->status->mark_synced( $post_id, $hash, 'auto' );
     711            } elseif ( $limit_hit ) {
     712                $this->status->mark_error( $post_id, __( 'Plan indexing limit reached', 'intufind' ) );
     713            }
     714        }
     715
     716        return $limit_hit;
    704717    }
    705718
     
    901914            unset( $doc['_hash'] );
    902915        }
     916        unset( $doc );
    903917
    904918        // Send to API.
     
    918932            $result['error_messages'][] = $error_message;
    919933
    920             // Signal plan limit so the caller can stop sending more batches.
    921934            if ( 'indexing_limit_exceeded' === $error_code ) {
    922935                $result['limit_reached'] = true;
    923936            }
    924937        } else {
    925             // Check for individual failures in response.
    926             $failed = $this->extract_failed_items( $response );
     938            $failed       = $this->extract_failed_items( $response );
     939            $success_ids  = isset( $response['data']['successfulIds'] ) ? $response['data']['successfulIds'] : array();
     940            $success_set  = array_flip( array_map( 'strval', $success_ids ) );
     941
     942            $limit_hit = ! empty( $response['data']['indexingLimitReached'] );
    927943
    928944            foreach ( $batch_ids as $post_id ) {
     
    932948                    $result['errors']++;
    933949                    $result['error_messages'][] = $error_msg;
    934                 } else {
     950                } elseif ( isset( $success_set[ (string) $post_id ] ) ) {
    935951                    $hash = $hashes[ (string) $post_id ] ?? '';
    936952                    $this->status->mark_synced( $post_id, $hash, 'manual' );
    937953                    $result['synced']++;
     954                } elseif ( $limit_hit ) {
     955                    $this->status->mark_error( $post_id, __( 'Plan indexing limit reached', 'intufind' ) );
     956                    $result['errors']++;
    938957                }
     958            }
     959
     960            if ( $limit_hit ) {
     961                $result['limit_reached'] = true;
    939962            }
    940963        }
  • intufind/trunk/intufind.php

    r3468114 r3469618  
    44 * Plugin URI: https://intufind.com/integrations/wordpress
    55 * Description: AI-powered search and chat for WordPress. Syncs your content to the cloud for semantic search, intelligent recommendations, and conversational AI.
    6  * Version: 1.2.2
     6 * Version: 1.2.3
    77 * Requires at least: 6.0
    88 * Requires PHP: 8.0
     
    2626 * Plugin constants.
    2727 */
    28 define( 'INTUFIND_VERSION', '1.2.2' );
     28define( 'INTUFIND_VERSION', '1.2.3' );
    2929define( 'INTUFIND_PLUGIN_FILE', __FILE__ );
    3030define( 'INTUFIND_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
  • intufind/trunk/readme.txt

    r3468114 r3469618  
    55Tested up to: 6.9
    66Requires PHP: 8.0
    7 Stable tag: 1.2.2
     7Stable tag: 1.2.3
    88WC tested up to: 9.6
    99License: GPLv2 or later
     
    215215== Changelog ==
    216216
     217= 1.2.3 =
     218* Fixed bulk sync failing entirely when batch exceeds plan indexing limit — now processes as many items as will fit
     219* Added retry with exponential backoff for rate-limited API requests during bulk sync
     220* Fixed error code detection for indexing limit responses
     221* Fixed _hash field leaking to API in real-time sync batches
     222
    217223= 1.2.2 =
    218224* Fixed RSS feed requests and bot-like URL patterns being logged as native search queries
     
    286292== Upgrade Notice ==
    287293
     294= 1.2.3 =
     295Fixes bulk sync failing entirely when plan limit is exceeded — items are now partially processed. Adds retry for rate-limited requests.
     296
    288297= 1.2.2 =
    289298Filters out RSS feed paths, URLs, and bot-like patterns from native search logging to keep analytics clean.
Note: See TracChangeset for help on using the changeset viewer.