Cotyledon

2.2.0 · active · verified Thu Apr 16

Cotyledon is a Python framework (version 2.2.0, actively maintained) designed for defining and managing long-running services. It provides robust handling of Unix signals, efficient spawning and supervision of worker processes, daemon reloading capabilities, `sd-notify` integration, and rate limiting for worker restarts. It sees significant use in OpenStack Telemetry projects as a lightweight replacement for `oslo.service`, which carried heavy `eventlet` dependencies. The library aims for a consistent code path for single and multiple worker configurations and offers advanced reload and termination APIs.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart defines a simple `MyService` that logs its lifecycle events (`init`, `run`, `terminate`, `reload`). The `ServiceManager` then registers two workers for this service. The `run` method simulates work, waiting on a shutdown event. To stop the service, send a `SIGTERM` (e.g., Ctrl+C in a terminal) to the main process; for reload, send `SIGHUP` (e.g., `kill -HUP <pid>`).

import cotyledon
import logging
import threading
import time
import os

LOG = logging.getLogger(__name__)

class MyService(cotyledon.Service):
    name = "my_example_service"

    def __init__(self, worker_id):
        super(MyService, self).__init__(worker_id)
        self._shutdown = threading.Event()
        LOG.info(f"[{os.getpid()}] {self.name} worker {self.worker_id} init")

    def run(self):
        LOG.info(f"[{os.getpid()}] {self.name} worker {self.worker_id} running...")
        # In a real service, this loop would perform work, e.g., consume from a queue
        # and call _shutdown.set() when it needs to stop processing.
        while not self._shutdown.is_set():
            LOG.debug(f"[{os.getpid()}] {self.name} worker {self.worker_id} working...")
            time.sleep(1)

    def terminate(self):
        LOG.info(f"[{os.getpid()}] {self.name} worker {self.worker_id} terminating...")
        self._shutdown.set()

    def reload(self):
        LOG.info(f"[{os.getpid()}] {self.name} worker {self.worker_id} reloading...")
        # Implement logic to reload configuration or re-initialize components
        # without stopping the worker if possible.

def main():
    # Basic setup for logging to console
    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(process)d - %(levelname)s - %(message)s')
    LOG.info("Starting Cotyledon Service Manager")

    manager = cotyledon.ServiceManager()
    # Add MyService with 2 worker processes
    manager.add(MyService, workers=2)
    # Run the service manager, which will spawn workers and handle signals
    manager.run()
    LOG.info("Cotyledon Service Manager stopped.")

if __name__ == "__main__":
    main()

view raw JSON →