meilisync

repository·dev·Indexed 18 days ago

https://github.com/long2ice/meilisync

A synchronization tool for real-time data transfer from MySQL, PostgreSQL, and MongoDB to MeiliSearch. It supports full initial loads and continuous incremental updates via event streams, featuring a CLI for managing sync processes, data consistency checks, and a plugin system for data transformation. Version 0.1.3.

Tokens
7.3K
Snippets
33
Records
36
Agent score
63%

What's inside meilisync

  1. Configure database prerequisites

    dev

    Before using meilisync, ensure your source database is configured correctly:

    • MySQL: Set binlog_format = ROW and ensure binary logging is enabled.
    • PostgreSQL: Set wal_level = logical and install the wal2json extension to enable logical replication.
    • MongoDB: Enable replica set mode to allow the use of change streams.
  2. Install meilisync

    dev

    Install meilisync via pip using extras to include support for your specific database type:

    • MySQL: pip install meilisync[mysql]
    • PostgreSQL: pip install meilisync[postgres]
    • MongoDB: pip install meilisync[mongo]
    • All databases: pip install meilisync[all]
    • Redis progress tracking: pip install meilisync[redis]
    pip install meilisync[mysql]
  3. Run meilisync with Docker

    dev

    You can run meilisync using Docker by mounting your config.yml file into the container. Use the following docker-compose.yml structure:

    version: "3"
    services:
      meilisync:
        image: long2ice/meilisync
        volumes:
          - ./config.yml:/meilisync/config.yml
        restart: always
    version: "3"
    services:
      meilisync:
        image: long2ice/meilisync
        volumes:
          - ./config.yml:/meilisync/config.yml
        restart: always
  4. Configure meilisync via config.yml

    dev

    The config.yml file defines how data is moved from your source to Meilisearch. Below is a breakdown of the main configuration sections:

    Global Settings

    • debug: (boolean) Set to true to enable verbose logging.
    • plugins: A list of Python modules to use as global plugins.
    • sentry: Configuration for Sentry error tracking (dsn and environment).

    progress

    Used to record the last sync position (e.g., MySQL binlog position).

    • type: file or redis.
    • path: (Required if type: file) File path to store progress (default: progress.json).
    • key: (Required if type: redis) Redis key (default: meilisync:progress).
    • dsn: Redis connection string (default: redis://localhost:6379/0).

    source

    Database connection settings.

    • type: mysql, postgres, or mongo.
    • database: The name of the database.
    • server_id: (MySQL only) Binlog server ID (default: 1).
    • host, port, user, password: Standard connection arguments.

    meilisearch

    Meilisearch connection and batching settings.

    • api_url: The Meilisearch API URL.
    • api_key: The Meilisearch API key.
    • insert_size: Number of documents to collect before performing an insert.
    • insert_interval: Number of seconds to wait before performing an insert.

    Note: If neither insert_size nor insert_interval is set, documents are inserted immediately. For better performance, increase these values.

    sync

    An array of sync tasks. Each task defines a mapping between a database table/collection and a Meilisearch index.

    • table: The source table or collection name.
    • index: The target Meilisearch index name (defaults to table name if omitted).
    • full: (boolean) Whether to perform a full sync (default: false).
    • fields: A mapping of {source_field: target_field}. If omitted, all fields are synced using their original names.
    • plugins: A list of table-level plugins.
    debug: true
    plugins:
      - meilisync.plugin.Plugin
    progress:
      type: file
    source:
      type: mysql
      host: 192.168.123.205
      port: 3306
      user: root
      password: "123456"
      database: beauty
    meilisearch:
      api_url: http://192.168.123.205:7700
      api_key: ""
      insert_size: 1000
      insert_interval: 10
    sync:
      - table: collection
        index: beauty-collections
        plugins:
          - meilisync.plugin.Plugin
        full: true
        fields:
          id:
          title:
          description:
          category:
      - table: picture
        index: beauty-pictures
        full: true
        fields:
          id:
          description:
          category
    sentry:
      dsn: ""
      environment: "production"
  5. Implement custom plugins for meilisync

    dev

    Plugins allow you to transform data before it is inserted into Meilisearch (pre_event) or after it has been inserted (post_event).

    To create a plugin, define a Python class with the following structure:

    class Plugin:
        is_global = False
    
        async def pre_event(self, event: Event):
            # Logic before Meilisearch insert
            return event
    
        async def post_event(self, event: Event):
            # Logic after Meilisearch insert
            return event

    Plugin Configuration

    • is_global: If set to True, the plugin instance is created only once for the entire process. If False, a new instance is created for every event. This can be configured in the plugins section of the config.yml (globally) or within a specific sync task (table-level).
    class Plugin:
        is_global = False
    
        async def pre_event(self, event: Event):
            logger.debug(f"pre_event: {event}, is_global: {self.is_global}")
            return event
    
        async def post_event(self, event: Event):
            logger.debug(f"post_event: {event}, is_global: {self.is_global}")
            return event
  6. Use the meilisync CLI

    dev

    The meilisync command-line interface provides several commands to manage synchronization. By default, it looks for a config.yml in the current directory.

    Commands

    • start: Begins the incremental synchronization process.
    • refresh -t <index_name>: Refreshes all data for a specific index by swapping indexes. Note: Stop the sync process before running this to avoid data inconsistency.
    • check -t <index_name>: Checks if the data count in the database is consistent with the data in Meilisearch.
    • version: Displays the current version of meilisync.

    Options

    • --config, -c <path>: Specify a custom configuration file path (default: config.yml).
    meilisync start
    meilisync refresh -t test
    meilisync check -t test
  7. Discover and retrieve Progress implementations with get_progress()

    dev

    Use get_progress(type_: ProgressType) to dynamically retrieve a specific Progress class implementation based on a ProgressType enum value. This is useful for selecting how synchronization progress is monitored or reported.

    from meilisync.discover import get_progress
    from meilisync.enums import ProgressType
    
    # Retrieve the class implementation for a specific progress type
    progress_class = get_progress(ProgressType.SOME_PROGRESS_TYPE)
    
    # Instantiate the progress handler
    instance = progress_class()
  8. Load a plugin from a module string

    dev

    The load_plugin function allows you to dynamically instantiate a plugin class using its full Python import path (e.g., package.module.ClassName). It uses importlib to import the module and then retrieves the specified class.

    from meilisync.plugin import load_plugin
    
    # Example usage: loading a class named 'MyPlugin' from 'my_package.plugins'
    plugin_class = load_plugin("my_package.plugins.MyPlugin")
    plugin_instance = plugin_class()
  9. Process a single event with handle_event()

    dev

    Use handle_event to process a single Event for a specific Sync configuration. It executes the following sequence:

    1. Runs pre_event hooks on all registered plugins.
    2. Performs the Meilisearch operation (add_documents, update_documents, or delete_documents) based on the EventType.
    3. Runs post_event hooks on all registered plugins.
    # event is an instance of Event, sync is an instance of Sync
    await meili.handle_event(event, sync)
  10. Initialize the Meili client

    dev

    The Meili class is the primary entry point for interacting with Meilisearch. It manages an asynchronous client and a collection of plugins that can intercept events.

    To initialize it, provide the api_url, api_key, and optionally a list of plugins (either instances of Plugin or their classes) and a wait_for_task_timeout in milliseconds.

    from meilisync.meili import Meili
    
    meili = Meili(
        api_url="http://localhost:7700",
        api_key="masterKey",
        plugins=[MyPlugin],
        wait_for_task_timeout=30000
    )
  11. Refresh Meilisearch index data using refresh_data()

    dev

    The refresh_data method performs an atomic index swap to refresh an entire index without downtime.

    It follows this workflow:

    1. Creates a temporary index named {index_name}_tmp.
    2. Copies the settings from the original index to the temporary index.
    3. Iterates through the provided AsyncGenerator of data, calling add_data for each batch.
    4. Waits for all insertion tasks to complete.
    5. Swaps the original index with the temporary index using swap_indexes.
    6. Deletes the temporary index.

    This is the recommended way to perform full re-indexes.

    async def my_data_generator():
        yield [{"id": 1, "name": "item1"}]
        yield [{"id": 2, "name": "item2"}]
    
    # sync is an instance of Sync configuration
    count = await meili.refresh_data(sync, my_data_generator())
    print(f"Refreshed {count} documents")
  12. Check if a Meilisearch index exists

    dev

    The index_exists method checks for the existence of a specific index in Meilisearch. It returns True if the index is found and False if the API returns a index_not_found error. Other errors are re-raised.

    exists = await meili.index_exists("my_index")
    if exists:
        print("Index is ready")