safefork


Namesafefork JSON
Version 1.0.0 PyPI version JSON
download
home_pageNone
SummaryUtilities for safely forking a process
upload_time2024-06-26 03:40:37
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseNone
keywords fork process python safe
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # safefork

Safefork is a small utility to check if your program is safe to be forked.


## Motivation

In production environments, it’s common for web servers like [Gunicorn](https://docs.gunicorn.org/en/stable/settings.html#preload-app) and background task processors like [Celery](https://github.com/celery/celery/blob/main/celery/concurrency/prefork.py#L1) to offer a forking option. This forking approach can save significant memory and reduce startup times, but it can be difficult to audit if your application is safe to fork. It's even more difficult to maintain this safety as your application grows. Did a recent PR introduce a threaded metrics collector? Or maybe a threaded database connection pool?

Safefork tries to alleviate this problem by checking some common issues that can arise when forking. It ensures that there are no active threads, no pending signals, and no running event loops, and that the garbage collector has been configured to optimize memory savings.


## Installation

```console
pip install safefork
```

## Example Usage: Flask + Gunicorn

```py
## app.py
import gc
import flask
import safefork

# Disable GC until after we fork to avoid "memory holes".
gc.disable()

def create_app():
    app = flask.Flask(__name__)
    initialize_systems()
    # Freeze an object generation to maximize shared memory across forked childs.
    gc.freeze()
    assert safefork.is_safe_to_fork(), "Uh-oh! The application is unsafe to fork"
    return app

## gunicorn.conf.py
import gc

def post_fork(server, worker):
    """
    Called just after a worker has been forked.
    Enables garbage collection in the worker process.
    """
    gc.enable()
    server.log.info("Garbage collection enabled in worker process after fork.")

bind = '0.0.0.0:8000'
workers = 4


## Command to start application:
## gunicorn -c gunicorn.conf.py 'app:create_app()'
```

## Contribution

Contributions are welcome! If you would like to contribute to this project, please include an example script in the unsafe_examples/ directory demonstrating how forking can be unsafe. This will help me and others understand potential issues. Appreciate your help in making this project better!


[![PyPI - Version](https://img.shields.io/pypi/v/safefork.svg)](https://pypi.org/project/safefork)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/safefork.svg)](https://pypi.org/project/safefork)

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "safefork",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "fork, process, python, safe",
    "author": null,
    "author_email": "Vivek Dasari <vivster7@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/be/aa/e0348b563891a6602312eed8b404c25d89d51295cee8cf40b9d7b4cfb6e4/safefork-1.0.0.tar.gz",
    "platform": null,
    "description": "# safefork\n\nSafefork is a small utility to check if your program is safe to be forked.\n\n\n## Motivation\n\nIn production environments, it\u2019s common for web servers like [Gunicorn](https://docs.gunicorn.org/en/stable/settings.html#preload-app) and background task processors like [Celery](https://github.com/celery/celery/blob/main/celery/concurrency/prefork.py#L1) to offer a forking option. This forking approach can save significant memory and reduce startup times, but it can be difficult to audit if your application is safe to fork. It's even more difficult to maintain this safety as your application grows. Did a recent PR introduce a threaded metrics collector? Or maybe a threaded database connection pool?\n\nSafefork tries to alleviate this problem by checking some common issues that can arise when forking. It ensures that there are no active threads, no pending signals, and no running event loops, and that the garbage collector has been configured to optimize memory savings.\n\n\n## Installation\n\n```console\npip install safefork\n```\n\n## Example Usage: Flask + Gunicorn\n\n```py\n## app.py\nimport gc\nimport flask\nimport safefork\n\n# Disable GC until after we fork to avoid \"memory holes\".\ngc.disable()\n\ndef create_app():\n    app = flask.Flask(__name__)\n    initialize_systems()\n    # Freeze an object generation to maximize shared memory across forked childs.\n    gc.freeze()\n    assert safefork.is_safe_to_fork(), \"Uh-oh! The application is unsafe to fork\"\n    return app\n\n## gunicorn.conf.py\nimport gc\n\ndef post_fork(server, worker):\n    \"\"\"\n    Called just after a worker has been forked.\n    Enables garbage collection in the worker process.\n    \"\"\"\n    gc.enable()\n    server.log.info(\"Garbage collection enabled in worker process after fork.\")\n\nbind = '0.0.0.0:8000'\nworkers = 4\n\n\n## Command to start application:\n## gunicorn -c gunicorn.conf.py 'app:create_app()'\n```\n\n## Contribution\n\nContributions are welcome! If you would like to contribute to this project, please include an example script in the unsafe_examples/ directory demonstrating how forking can be unsafe. This will help me and others understand potential issues. Appreciate your help in making this project better!\n\n\n[![PyPI - Version](https://img.shields.io/pypi/v/safefork.svg)](https://pypi.org/project/safefork)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/safefork.svg)](https://pypi.org/project/safefork)\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Utilities for safely forking a process",
    "version": "1.0.0",
    "project_urls": {
        "Documentation": "https://github.com/vivster7/safefork#readme",
        "Issues": "https://github.com/vivster7/safefork/issues",
        "Source": "https://github.com/vivster7/safefork"
    },
    "split_keywords": [
        "fork",
        " process",
        " python",
        " safe"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "ecc12fb373f629f4d4d2fdfca4f241928824dd0c8c850a8e265eb02fc3722ae4",
                "md5": "db7c57bc9001b1273454edfde8fae5b2",
                "sha256": "5943744e4980e3d06ad9780907c85be327deafee34121dcd2ef62dea6117cc56"
            },
            "downloads": -1,
            "filename": "safefork-1.0.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "db7c57bc9001b1273454edfde8fae5b2",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 4369,
            "upload_time": "2024-06-26T03:40:35",
            "upload_time_iso_8601": "2024-06-26T03:40:35.680218Z",
            "url": "https://files.pythonhosted.org/packages/ec/c1/2fb373f629f4d4d2fdfca4f241928824dd0c8c850a8e265eb02fc3722ae4/safefork-1.0.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "beaae0348b563891a6602312eed8b404c25d89d51295cee8cf40b9d7b4cfb6e4",
                "md5": "26872696677bff4d0f60c2067b3e4287",
                "sha256": "7989e299b51e0825d8cabecd58b51ddb4a92bfa4da6543887a6c6ef38da8cbd5"
            },
            "downloads": -1,
            "filename": "safefork-1.0.0.tar.gz",
            "has_sig": false,
            "md5_digest": "26872696677bff4d0f60c2067b3e4287",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 6138,
            "upload_time": "2024-06-26T03:40:37",
            "upload_time_iso_8601": "2024-06-26T03:40:37.598391Z",
            "url": "https://files.pythonhosted.org/packages/be/aa/e0348b563891a6602312eed8b404c25d89d51295cee8cf40b9d7b4cfb6e4/safefork-1.0.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-06-26 03:40:37",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "vivster7",
    "github_project": "safefork#readme",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "safefork"
}
        
Elapsed time: 0.40417s