Multiple services
DipDup provides several ways to run multiple services as a part of a single application. This is useful for running background tasks, heavy computations, or any other long-running processes that should not block the main DipDup service.
This table summarizes the available methods:
method | description | process | database |
---|---|---|---|
Job scheduler | Add definition to jobs config section | same process | same database |
CLI command | Implement a CLI command to extend dipdup tool | separate process | same or separate database |
Isolated package | Create a separate package with its own dipdup config | separate process | separate database |
Job scheduler
DipDup has a built-in job scheduler that allows you to run tasks on schedule or in background.
Here's a minimal configuration for long-running service:
hooks:
service:
callback: service
jobs:
service_daemon:
hook: service
daemon: true
Run dipdup init
after adding this configuration to create a callback stub in hooks
directory:
DipDup jobs are executed in the same process, thread and asyncio event loop as the main DipDup service, and use the same database connection. You can use ctx
to communicate with indexer and access its state.
CLI command
Another approach is to implement a custom CLI command that runs in a separate process, but still uses the same DipDup configuration and environment variables. This is useful for running heavy computations or background tasks that do not need to be part of the main indexing process.
First, create a new file cli.py
in the project root directory:
from contextlib import AsyncExitStack
import click
from dipdup.cli import cli, _cli_wrapper
from dipdup.config import DipDupConfig
from dipdup.context import DipDupContext
from dipdup.utils.database import tortoise_wrapper
@cli.command(help='Run heavy calculations')
@click.option('--key', help='Example option', required=True)
@click.pass_context
@_cli_wrapper
async def heavy_stuff(ctx, key: str) -> None:
config: DipDupConfig = ctx.obj.config
async with tortoise_wrapper(
url=config.database.connection_string,
models=f'{config.package}.models',
):
...
if __name__ == '__main__':
cli(prog_name='dipdup', standalone_mode=True)
Then use python -m dipdup_indexer.cli
instead of dipdup
as an entrypoint. Now you can call heavy-stuff
like one of DipDup commands. dipdup.cli:cli
group handles arguments and config parsing, graceful shutdown, and other boilerplate.
python -m dipdup_indexer.cli heavy-stuff --key value
If your service is long-running, you need to update Dockerfile and Compose manifests. Copy deploy/Dockerfile
to deploy/Dockerfile.service
and add the following lines to the end of the file:
ENTRYPOINT ["python", "-m", "dipdup_indexer.cli"]
CMD ["heavy-stuff", "--key", "value"]
For Docker Compose, add a new service to deploy/compose.yaml
:
services:
dipdup_service:
build:
context: .
dockerfile: deploy/Dockerfile.service
...
Run make up
to update the stack.
Isolated package
You can also create a separate package with its own dipdup.yaml
configuration file and reuse only the necessary parts of the project. Useful for monorepos with multiple DipDup projects.
New project directory can be either a sibling of the main project or a subdirectory. In this example, we will create a nested package dipdup_indexer/service
# Create a new package with its own DipDup config
dipdup new --name service
# Move to the service directory
cd service
make install
source .venv/bin/activate