cmsig/seal-read-write-adapter
SEAL Read Write Adapter lets you use separate search adapters for reads and writes, enabling reindexing without downtime. Combine any AdapterInterface implementations (e.g., Elasticsearch) and configure via DSN for your framework.
Install Dependencies:
composer require cmsig/seal cmsig/seal-read-write-adapter
Define Adapters: Create separate instances for read and write operations (e.g., Elasticsearch, PostgreSQL, etc.):
$readAdapter = new \CmsIg\Seal\Adapter\Elasticsearch\ElasticsearchAdapter(
$readClientConfig
);
$writeAdapter = new \CmsIg\Seal\Adapter\Elasticsearch\ElasticsearchAdapter(
$writeClientConfig
);
Wrap with ReadWriteAdapter:
use CmsIg\Seal\Adapter\ReadWrite\ReadWriteAdapter;
$adapter = new ReadWriteAdapter($readAdapter, $writeAdapter);
Initialize SEAL Engine:
$engine = new \CmsIg\Seal\Engine($adapter, $schema);
Zero-Downtime Reindexing:
writeAdapter to index new/updated data into a staging index (or separate DB).readAdapter to serve queries from the live index (or primary DB).readAdapter to point to the new index.Write Operations:
create, update, and delete calls route to $writeAdapter.$engine->create($document); // Writes to $writeAdapter
Read Operations:
search, get, and exists calls route to $readAdapter.$results = $engine->search($query); // Reads from $readAdapter
Synchronization:
$writeAdapter → $readAdapter asynchronously.public function handle() {
$documents = $writeAdapter->getUnsyncedDocuments();
foreach ($documents as $doc) {
$readAdapter->upsert($doc);
}
}
Laravel Service Providers:
Bind adapters and the ReadWriteAdapter to the container:
$this->app->singleton('readAdapter', fn() => new ElasticsearchAdapter($readConfig));
$this->app->singleton('writeAdapter', fn() => new ElasticsearchAdapter($writeConfig));
$this->app->singleton('seal.adapter', fn($app) =>
new ReadWriteAdapter($app['readAdapter'], $app['writeAdapter'])
);
Configuration: Use environment variables for adapter-specific configs:
SEAL_READ_URL=elasticsearch://read-host:9200
SEAL_WRITE_URL=elasticsearch://write-host:9200
Fallback Logic:
Implement a fallback to $writeAdapter for reads during sync failures:
$adapter = new ReadWriteAdapter($readAdapter, $writeAdapter, true); // Enable fallback
Stale Reads:
$readAdapter may return outdated data if not synced.$writeAdapter before updates.Adapter Inconsistency:
$readAdapter and $writeAdapter might have different schemas or mappings.Performance Overhead:
Log Adapter Calls: Decorate adapters to log operations:
$readAdapter = new \CmsIg\Seal\Adapter\LoggingAdapter($readAdapter);
Verify Sync Status: Add a health check endpoint:
Route::get('/sync-status', function () {
return [
'read_write_lag' => $writeAdapter->count() - $readAdapter->count(),
];
});
Custom Sync Logic:
Override ReadWriteAdapter to add pre/post-sync hooks:
class CustomReadWriteAdapter extends ReadWriteAdapter {
public function sync() {
$this->preSync();
parent::sync();
$this->postSync();
}
}
Multi-Write Support:
Use MultiAdapter (mentioned in README) to write to both adapters atomically:
$multiAdapter = new \CmsIg\Seal\Adapter\MultiAdapter([$readAdapter, $writeAdapter]);
Adapter-Specific Features:
Leverage adapter-specific optimizations (e.g., Elasticsearch’s refresh parameter):
$writeAdapter = new ElasticsearchAdapter($config, ['refresh' => 'false']);
multi:// and read-write:// DSNs are experimental. Prefer explicit instantiation for reliability.How can I help you explore Laravel packages today?