Skip to content

Pass abortSignal through SearchRequestAdapter to multiSearch.perform() #266

@isaacrowntree

Description

@isaacrowntree

Problem

When using the adapter with InstantSearch's geo search (e.g. useGeoSearch with map panning), rapid refine() calls can stack up many concurrent multiSearch.perform() requests. If a search takes longer than the debounce interval, previous in-flight requests aren't cancelled — they all run to completion (or timeout), wasting resources and potentially returning stale results.

Context

The Typesense JS SDK already supports request cancellation via AbortSignal:

  • MultiSearch.perform(searches, commonParams, options) accepts options.abortSignal (PR #297, shipped in v2.1.0)
  • ApiCall translates the signal to an Axios CancelToken and properly handles node health tracking for aborted requests (PR #253)

However, SearchRequestAdapter.request() calls multiSearch.perform() with only two arguments:

// SearchRequestAdapter.js, request() method
return this.typesenseClient.multiSearch.perform(searchRequest, commonParams);
//                                                                          ^ no 3rd arg

The third options parameter (where abortSignal would go) is never passed through.

Proposed Solution

Option A: Internal AbortController management (recommended)

Since the Algolia SearchClient interface doesn't pass an AbortSignal to search(), the adapter could manage its own AbortController internally — aborting the previous in-flight search when a new one starts:

// In TypesenseInstantsearchAdapter
let activeController = null;

async searchTypesenseAndAdapt(instantsearchRequests) {
  if (activeController) activeController.abort();
  const controller = new AbortController();
  activeController = controller;

  try {
    // ... existing logic, but pass abortSignal through to request()
    return await this._adaptAndPerformTypesenseRequest(
      instantsearchRequests,
      { abortSignal: controller.signal }
    );
  } finally {
    if (activeController === controller) activeController = null;
  }
}

Option B: Pass-through only

At minimum, expose an options parameter on SearchRequestAdapter.request() and forward it to multiSearch.perform(), allowing consumers to manage their own AbortController:

async request(options) {
  // ... existing search building logic ...
  return this.typesenseClient.multiSearch.perform(searchRequest, commonParams, options);
}

Impact

In our production app, a single user panning a map generates 10-15 concurrent requests that all timeout after 2s. This produces the majority of our Typesense-related errors. Adding cancellation at this layer would eliminate the issue.

Current Workaround

We're monkey-patching adapter.typesenseClient.multiSearch.perform after construction to inject an AbortController per call (also wrapping clearCache to re-apply after client replacement). Works, but would be cleaner as a first-class feature.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions