Skip to content

Scout indexes synced with stale data during transaction #1906

Closed
@bakerkretzmar

Description

@bakerkretzmar

With default Nova and Scout setups, model data is sometimes synced to the search index before it's done being saved, which results in the old data being indexed.

  • Laravel Version: 6.0.1
  • Nova Version: 2.2.0
  • PHP Version: 7.2
  • Database Driver and Version: MySQL 8.0.16
  • Operating System and Version: macOS 10.13

Description

Because Nova updates resources inside a transaction, the saved model event is sometimes fired before the new data has been committed and persisted to the database. Scout uses the saved event to trigger updating the search index, so when that update happens, if the new model data hasn't been persisted yet, the search index will be updated with the old data.

See #1759, laravel/scout#152, laravel/framework#8627, laravel/framework#29710, and laravel/ideas#1441.

I would think this is more of a Scout issue, since it would make sense to me to only bother persisting data to a search index once we're absolutely certain that that data is what's saved in our records (i.e. after all transactions are committed), cc @driesvints. Scout hooking updates into the saved event makes that kind of impossible if you use database transactions. Setting scout.queue to false doesn't help here either.

Is it really unreasonable to suggest not using a database transaction to update Nova resources?? Not sure how else we'd get around this.

Steps To Reproduce

  1. Fresh Laravel, Nova, and Scout installs.
  2. Set up Algolia so that Scout works and you can verify the data being synce (not necessary if you just want to see what order things happen in locally).
  3. Create a model observer and set up the saved method to dump something out.
  4. Open up Laravel\Scout\Searchable and dump something from within the queueMakeSearchable method (or from Laravel\Scout\Jobs\MakeSearchable).

Watch dumps: queueMakeSearchable is executed before your own model observer's saved event.

Ugly workaround

I don't know why this works, but if I register an observer to run only during Nova requests rather than globally, things execute in the order I want. For now, this workaround seems to be holding:

// in Nova-specific model observer

public function saving(Model $model)
{
    Model::disableSearchSyncing();
}

public function saved(Model $model)
{
    $model->searchable();
}

I'm turning off search syncing on that model during Nova requests, and then making the model searchable manually after the fact.

🙃

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