jupyterhub-idle-culler


Namejupyterhub-idle-culler JSON
Version 1.3.0 PyPI version JSON
download
home_page
SummaryJupyterHub Python repository template
upload_time2024-02-26 08:12:03
maintainer
docs_urlNone
author
requires_python>=3.7
license# The Jupyter multi-user notebook server licensing terms Jupyter multi-user notebook server is licensed under the terms of the Modified BSD License (also known as New or Revised or 3-Clause BSD), as follows: - Copyright (c) 2014-, Jupyter Development Team All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Jupyter Development Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## About the Jupyter Development Team The Jupyter Development Team is the set of all contributors to the Jupyter project. This includes all of the Jupyter subprojects. The core team that coordinates development on GitHub can be found here: https://github.com/jupyter/. ## Our Copyright Policy Jupyter uses a shared copyright model. Each contributor maintains copyright over their contributions to Jupyter. But, it is important to note that these contributions are typically only changes to the repositories. Thus, the Jupyter source code, in its entirety is not the copyright of any single person or institution. Instead, it is the collective copyright of the entire Jupyter Development Team. If individual contributors want to maintain a record of what changes/contributions they have specific copyright on, they should indicate their copyright in the commit message of the change, when they commit the change to one of the Jupyter repositories. With this in mind, the following banner should be used in any source code file to indicate the copyright and license terms: # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License.
keywords jupyterhub
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # JupyterHub Idle Culler Service

[![GitHub Workflow Status - Test](https://img.shields.io/github/actions/workflow/status/jupyterhub/jupyterhub-idle-culler/test.yaml?logo=github&label=tests)](https://github.com/jupyterhub/jupyterhub-idle-culler/actions)
[![Latest PyPI version](https://img.shields.io/pypi/v/jupyterhub-idle-culler?logo=pypi&logoColor=white)](https://pypi.python.org/pypi/jupyterhub-idle-culler)
[![GitHub](https://img.shields.io/badge/issue_tracking-github-blue?logo=github)](https://github.com/jupyterhub/jupyterhub-idle-culler/issues)
[![Discourse](https://img.shields.io/badge/help_forum-discourse-blue?logo=discourse)](https://discourse.jupyter.org/c/jupyterhub)
[![Gitter](https://img.shields.io/badge/social_chat-gitter-blue?logo=gitter)](https://gitter.im/jupyterhub/jupyterhub)

`jupyterhub-idle-culler` provides a JupyterHub service to identify and stop idle
or long-running Jupyter servers via JupyterHub. It works solely by interacting
with JupyterHub's REST API, and is often configured to run as a JupyterHub
managed service started up by JupyterHub itself.

## Setup

Setup involves three parts:

1. Install the Python package.
2. Configure JupyterHub permissions to work against JupyterHub's REST API.
3. Configure how its started up, either as a JupyterHub managed service or as a standalone script.

### Installation

```bash
pip install jupyterhub-idle-culler
```

### Permissions

Prior to JupyterHub 2.0, the `jupyterhub-idle-culler` required full administrative privileges,
in order to have sufficient permissions to stop servers on behalf of users.

JupyterHub 2.0 introduces [scopes][] to allow for more fine-grained permission control.
This means that the configured culler service does not need full administrative privileges anymore.
It can be assigned only the permissions it needs.

[scopes]: https://jupyterhub.readthedocs.io/en/latest/rbac/scopes.html#available-scopes

`jupyterhub-idle-culler` requires the following scopes to function:

- `list:users` - to access to the user list API, our source of information about who to cull
- `read:users:activity` - to read the users' `last_activity` field
- `read:servers` - to read the users' `servers` field
- `delete:servers` - to stop users' servers, and delete named servers if `--remove-named-servers` is passed
- `admin:users` (**optional**) - to delete users if `--cull-users` is passed

To assign the service the appropriate permissions, declare a role in your `jupyterhub_config.py`:

```python
c.JupyterHub.load_roles = [
    {
        "name": "jupyterhub-idle-culler-role",
        "scopes": [
            "list:users",
            "read:users:activity",
            "read:servers",
            "delete:servers",
            # "admin:users", # if using --cull-users
        ],
        # assignment of role's permissions to:
        "services": ["jupyterhub-idle-culler-service"],
    }
]
```

### As a hub managed service

In `jupyterhub_config.py`, add the following dictionary for the idle-culler
service to the `c.JupyterHub.services` list:

```python
c.JupyterHub.services = [
    {
        "name": "jupyterhub-idle-culler-service",
        "command": [
            sys.executable,
            "-m", "jupyterhub_idle_culler",
            "--timeout=3600",
        ],
        # "admin": True,
    }
]
```

where:

- `"command"` indicates that the Service will be managed by the Hub, and
- `"admin": True` grants admin permissions to this Service and is only meant for
  use with jupyterhub < 2.0; see [above][permissions].

### As a standalone script

`jupyterhub-idle-culler` can also be run as a standalone script. It can
access the hub's api with a service token.

Register the service token with JupyterHub in `jupyterhub_config.py`:

```python
c.JupyterHub.services = [
    {
        "name": "jupyterhub-idle-culler-service",
        "api_token": "...",
        # "admin": True,
    }
]
```

where:

- `"api_token"` contains a secret token, e.g. generated by `openssl rand -hex 32`, and
- `"admin": True` grants admin permissions to this Service and is only meant for
  use with jupyterhub < 2.0; see [above][permissions].

and store the same token in a `JUPYTERHUB_API_TOKEN` environment variable.
Then start `jupyterhub-idle-culler` manually.

```bash
export JUPYTERHUB_API_TOKEN=api_token_above...
python3 -m jupyterhub_idle_culler [--timeout=900] [--url=http://localhost:8081/hub/api]
```

## Command line flags

```
  --api-page-size                  Number of users to request per page, when
                                   using JupyterHub 2.0's paginated user list
                                   API. Default: user the server-side default
                                   configured page size. (default 0)
  --concurrency                    Limit the number of concurrent requests made
                                   to the Hub.  Deleting a lot of users at the
                                   same time can slow down the Hub, so limit
                                   the number of API requests we have
                                   outstanding at any given time. (default 10)
  --cull-admin-users               Whether admin users should be culled (only
                                   if --cull-users=true). (default True)
  --cull-every                     The interval (in seconds) for checking for
                                   idle servers to cull. (default 0)
  --cull-users                     Cull users in addition to servers.  This is
                                   for use in temporary-user cases such as
                                   tmpnb. (default False)
  --internal-certs-location        The location of generated internal-ssl
                                   certificates (only needed with --ssl-
                                   enabled=true). (default internal-ssl)
  --max-age                        The maximum age (in seconds) of servers that
                                   should be culled even if they are active.
                                   (default 0)
  --remove-named-servers           Remove named servers in addition to stopping
                                   them.  This is useful for a BinderHub that
                                   uses authentication and named servers.
                                   (default False)
  --ssl-enabled                    Whether the Jupyter API endpoint has TLS
                                   enabled. (default False)
  --timeout                        The idle timeout (in seconds). (default 600)
  --url                            The JupyterHub API URL.
```

## Caveats

1. JupyterHub's `last_activity` data about user servers is not updated with high
   frequency, so cull timeout should be greater than the sum of:

   - single-user websocket ping interval (default: 30 seconds)
   - `JupyterHub.last_activity_interval` (default: 5 minutes)

2. If you want to use `--cull-users` with a different culling interval for the
   user servers and users, you must start two idle culler services. This is
   because both are configured via `--timeout` and `--max-age`. To do so,
   configure this service to start twice with different configuration, where one
   has the `--cull-users` option.

3. By default `jupyterhub-idle-cullers` HTTP requests to JupyterHub's REST API
   timeouts after 60 seconds. This can be changed by setting the
   `JUPYTERHUB_REQUEST_TIMEOUT` environment variable.

## How it works

JupyterHub's REST API is used to acquire information about activity, and if the
idle culler service based on configuration thinks a server should be stopped or
deleted it also does so via JupyterHub's REST API.

### In depth

`jupyterhub-idle-culler` relies on permission to work against JupyterHub's REST
API is provided via the `JUPYTERHUB_API_TOKEN`, that is set automatically for
[managed services] started by JupyterHub.

`jupyterhub-idle-culler` lists available users and their server's reported
`last_activity` via JupyterHub's [`/users`] REST API and makes decisions based on
that. User's default servers can be stopped via [`/users/{name}/server`], named
servers can be stopped and optionally removed via
[`/users/{name}/servers/{server_name}`], and users can optionally be deleted via
[`/users/{name}`].

[managed services]: https://jupyterhub.readthedocs.io/en/stable/reference/services.html#launching-a-hub-managed-service
[`/users`]: https://jupyterhub.readthedocs.io/en/stable/reference/rest-api.html#/default/get_users
[`/users/{name}/server`]: https://jupyterhub.readthedocs.io/en/stable/reference/rest-api.html#/default/delete_users__name__server
[`/users/{name}/servers/{server_name}`]: https://jupyterhub.readthedocs.io/en/stable/reference/rest-api/index.html#operation--users--name--servers--server_name--delete
[`/users/{name}`]: https://jupyterhub.readthedocs.io/en/stable/reference/rest-api.html#/default/delete_users__name_

JupyterHub's reported `last_activity` for user servers is updated by JupyterHub
at a regular interval in the [`update_last_activity` function] that relies on
two sources of information.

[`update_last_activity` function]: https://github.com/jupyterhub/jupyterhub/blob/3.1.1/jupyterhub/app.py#L3002

1. **The proxy's routes data**

   The configurable proxy class for JupyterHub is an interface for JupyterHub to
   request routing of network traffic to user servers. Through this interface,
   JupyterHub be informed on network activity if the proxy class provides it,
   specifically via the [`get_all_routes`] function.

   The [configurable-http-proxy] used in https://z2jh.jupyter.org provides
   information about network routes activity, but [traefik-proxy] used in
   https://tljh.jupyter.org [currently does not].

   [`get_all_routes`]: https://jupyterhub.readthedocs.io/en/stable/reference/proxy.html#retrieving-routes
   [configurable-http-proxy]: https://github.com/jupyterhub/configurable-http-proxy#readme
   [traefik-proxy]: https://github.com/jupyterhub/traefik-proxy#readme
   [currently does not]: https://github.com/jupyterhub/traefik-proxy/issues/151

2. **The user server's activity reports**

   The `update_last_activity` function also reads JupyterHub's database that
   keeps state about servers `last_activity`. These database records are updated
   whenever a server notifies JupyterHub about activity, as they are required to
   do.

   Servers has before JupyterHub 4 notified JupyterHub about activity by being
   started by the [`jupyterhub-singleuser`] script made available by installing
   `jupyterhub` (or `jupyterhub-singleuser` on conda-forge). With JupyterHub 4+
   and jupyter_server 2+ a jupyter_server server extension can be used instead.

   The `jupyterhub-singleuser` script launches a modified server application
   that keeps JupyterHub updated with the server activity via the
   [`notify_activity`] function.

   The `notify_activity` function in turn make use of the server applications
   `last_activity` function (see implementation in [NotebookApp] and [ServerApp]
   respectively) that that combines information from API activity, kernel
   activity, kernel shutdown, and terminal activity. This activity also covers
   activity of applications like RStudio running via `jupyter-server-proxy`.

   [`jupyterhub-singleuser`]: https://github.com/jupyterhub/jupyterhub/blob/3.1.1/setup.py#L112
   [`notify_activity`]: https://github.com/jupyterhub/jupyterhub/blob/3.1.1/jupyterhub/singleuser/mixins.py#L532
   [notebookapp]: https://github.com/jupyter/notebook/blob/v6.5.2/notebook/notebookapp.py#L391-L396
   [serverapp]: https://github.com/jupyter-server/jupyter_server/blob/v1.23.5/jupyter_server/serverapp.py#L446-L451

Here is a summary of what's described so far:

1. `jupyterhub-idle-culler` collects information and acts entirely through
   JupyterHub's REST API.
2. `jupyterhub-idle-culler` makes decisions based on information provided by
   JupyterHub, that collects activity reports from the user servers and polls
   the proxy class for information about user servers' network activity.

Now, as the server's kernel activity influence the activity that servers will
notify JupyterHub about, the kernel activity in turn influences
`jupyterhub-idle-culler`. Due to this, it can be relevant to also learn a little
about a mechanism to _cull idle kernels_ as well even though
`jupyterhub-idle-culler` isn't involved in that.

The default kernel manager, the `MappingKernelManager`, can be configured to
cull idle kernels. Its configuration is documented in
[ServerApp's](https://jupyter-server.readthedocs.io/en/stable/other/full-config.html)
and
[NotebookApp's](https://jupyter-notebook.readthedocs.io/en/stable/config.html#options)
respective documentation, and here are some relevant kernel culling
configuration options:

- `MappingKernelManager.cull_busy`
- `MappingKernelManager.cull_idle_timeout`
- `MappingKernelManager.cull_interval`
- `MappingKernelManager.cull_connected`

  Note that `cull_connected` can be tricky to understand for JupyterLab as a
  browser having a web-socket connection to a kernel or not isn't as obvious as
  it was in the classical Jupyter notebook UI. See [this issue for more
  details](https://github.com/jupyterlab/jupyterlab/issues/6893).

  Also note that configuration of MappingKernelManager should be made on the
  user server itself, for example via a `jupyter_server_config.py` file in
  `/etc/jupyter` or `/usr/local/etc/jupyter` rather than where JupyterHub is
  running.

Finally, note that a Jupyter server can shut itself down without intervention by
`jupyterhub-idle-culler` if `ServerApp.shutdown_no_activity_timeout` is
configured.

### Caveats

#### Pagination

JupyterHub 2.0 introduces pagination to the [`/users`] API endpoint. This
pagination does not guarantee a consistent snapshot for consecutive requests
spread over time, so it is possible for a highly active hub to occasionally miss
culling users crossing page boundaries between requests. This is expected to be
an infrequent occurrence and only result in delaying a server being culled by
one cull interval in realistic scenarios, so of minor consequence in JupyterHub.

The issue can be mitigated by requesting a larger page size, via e.g.
`--api-page-size=200`, but feel free to open an issue if this is causing a
problem for you.

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "jupyterhub-idle-culler",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "jupyterhub",
    "author": "",
    "author_email": "Jupyter Contributors <jupyter@googlegroups.com>",
    "download_url": "https://files.pythonhosted.org/packages/d0/99/9a063b360d8f641435c0559a74b08c710ea2e9fff224e1ac0b676449b178/jupyterhub_idle_culler-1.3.0.tar.gz",
    "platform": null,
    "description": "# JupyterHub Idle Culler Service\n\n[![GitHub Workflow Status - Test](https://img.shields.io/github/actions/workflow/status/jupyterhub/jupyterhub-idle-culler/test.yaml?logo=github&label=tests)](https://github.com/jupyterhub/jupyterhub-idle-culler/actions)\n[![Latest PyPI version](https://img.shields.io/pypi/v/jupyterhub-idle-culler?logo=pypi&logoColor=white)](https://pypi.python.org/pypi/jupyterhub-idle-culler)\n[![GitHub](https://img.shields.io/badge/issue_tracking-github-blue?logo=github)](https://github.com/jupyterhub/jupyterhub-idle-culler/issues)\n[![Discourse](https://img.shields.io/badge/help_forum-discourse-blue?logo=discourse)](https://discourse.jupyter.org/c/jupyterhub)\n[![Gitter](https://img.shields.io/badge/social_chat-gitter-blue?logo=gitter)](https://gitter.im/jupyterhub/jupyterhub)\n\n`jupyterhub-idle-culler` provides a JupyterHub service to identify and stop idle\nor long-running Jupyter servers via JupyterHub. It works solely by interacting\nwith JupyterHub's REST API, and is often configured to run as a JupyterHub\nmanaged service started up by JupyterHub itself.\n\n## Setup\n\nSetup involves three parts:\n\n1. Install the Python package.\n2. Configure JupyterHub permissions to work against JupyterHub's REST API.\n3. Configure how its started up, either as a JupyterHub managed service or as a standalone script.\n\n### Installation\n\n```bash\npip install jupyterhub-idle-culler\n```\n\n### Permissions\n\nPrior to JupyterHub 2.0, the `jupyterhub-idle-culler` required full administrative privileges,\nin order to have sufficient permissions to stop servers on behalf of users.\n\nJupyterHub 2.0 introduces [scopes][] to allow for more fine-grained permission control.\nThis means that the configured culler service does not need full administrative privileges anymore.\nIt can be assigned only the permissions it needs.\n\n[scopes]: https://jupyterhub.readthedocs.io/en/latest/rbac/scopes.html#available-scopes\n\n`jupyterhub-idle-culler` requires the following scopes to function:\n\n- `list:users` - to access to the user list API, our source of information about who to cull\n- `read:users:activity` - to read the users' `last_activity` field\n- `read:servers` - to read the users' `servers` field\n- `delete:servers` - to stop users' servers, and delete named servers if `--remove-named-servers` is passed\n- `admin:users` (**optional**) - to delete users if `--cull-users` is passed\n\nTo assign the service the appropriate permissions, declare a role in your `jupyterhub_config.py`:\n\n```python\nc.JupyterHub.load_roles = [\n    {\n        \"name\": \"jupyterhub-idle-culler-role\",\n        \"scopes\": [\n            \"list:users\",\n            \"read:users:activity\",\n            \"read:servers\",\n            \"delete:servers\",\n            # \"admin:users\", # if using --cull-users\n        ],\n        # assignment of role's permissions to:\n        \"services\": [\"jupyterhub-idle-culler-service\"],\n    }\n]\n```\n\n### As a hub managed service\n\nIn `jupyterhub_config.py`, add the following dictionary for the idle-culler\nservice to the `c.JupyterHub.services` list:\n\n```python\nc.JupyterHub.services = [\n    {\n        \"name\": \"jupyterhub-idle-culler-service\",\n        \"command\": [\n            sys.executable,\n            \"-m\", \"jupyterhub_idle_culler\",\n            \"--timeout=3600\",\n        ],\n        # \"admin\": True,\n    }\n]\n```\n\nwhere:\n\n- `\"command\"` indicates that the Service will be managed by the Hub, and\n- `\"admin\": True` grants admin permissions to this Service and is only meant for\n  use with jupyterhub < 2.0; see [above][permissions].\n\n### As a standalone script\n\n`jupyterhub-idle-culler` can also be run as a standalone script. It can\naccess the hub's api with a service token.\n\nRegister the service token with JupyterHub in `jupyterhub_config.py`:\n\n```python\nc.JupyterHub.services = [\n    {\n        \"name\": \"jupyterhub-idle-culler-service\",\n        \"api_token\": \"...\",\n        # \"admin\": True,\n    }\n]\n```\n\nwhere:\n\n- `\"api_token\"` contains a secret token, e.g. generated by `openssl rand -hex 32`, and\n- `\"admin\": True` grants admin permissions to this Service and is only meant for\n  use with jupyterhub < 2.0; see [above][permissions].\n\nand store the same token in a `JUPYTERHUB_API_TOKEN` environment variable.\nThen start `jupyterhub-idle-culler` manually.\n\n```bash\nexport JUPYTERHUB_API_TOKEN=api_token_above...\npython3 -m jupyterhub_idle_culler [--timeout=900] [--url=http://localhost:8081/hub/api]\n```\n\n## Command line flags\n\n```\n  --api-page-size                  Number of users to request per page, when\n                                   using JupyterHub 2.0's paginated user list\n                                   API. Default: user the server-side default\n                                   configured page size. (default 0)\n  --concurrency                    Limit the number of concurrent requests made\n                                   to the Hub.  Deleting a lot of users at the\n                                   same time can slow down the Hub, so limit\n                                   the number of API requests we have\n                                   outstanding at any given time. (default 10)\n  --cull-admin-users               Whether admin users should be culled (only\n                                   if --cull-users=true). (default True)\n  --cull-every                     The interval (in seconds) for checking for\n                                   idle servers to cull. (default 0)\n  --cull-users                     Cull users in addition to servers.  This is\n                                   for use in temporary-user cases such as\n                                   tmpnb. (default False)\n  --internal-certs-location        The location of generated internal-ssl\n                                   certificates (only needed with --ssl-\n                                   enabled=true). (default internal-ssl)\n  --max-age                        The maximum age (in seconds) of servers that\n                                   should be culled even if they are active.\n                                   (default 0)\n  --remove-named-servers           Remove named servers in addition to stopping\n                                   them.  This is useful for a BinderHub that\n                                   uses authentication and named servers.\n                                   (default False)\n  --ssl-enabled                    Whether the Jupyter API endpoint has TLS\n                                   enabled. (default False)\n  --timeout                        The idle timeout (in seconds). (default 600)\n  --url                            The JupyterHub API URL.\n```\n\n## Caveats\n\n1. JupyterHub's `last_activity` data about user servers is not updated with high\n   frequency, so cull timeout should be greater than the sum of:\n\n   - single-user websocket ping interval (default: 30 seconds)\n   - `JupyterHub.last_activity_interval` (default: 5 minutes)\n\n2. If you want to use `--cull-users` with a different culling interval for the\n   user servers and users, you must start two idle culler services. This is\n   because both are configured via `--timeout` and `--max-age`. To do so,\n   configure this service to start twice with different configuration, where one\n   has the `--cull-users` option.\n\n3. By default `jupyterhub-idle-cullers` HTTP requests to JupyterHub's REST API\n   timeouts after 60 seconds. This can be changed by setting the\n   `JUPYTERHUB_REQUEST_TIMEOUT` environment variable.\n\n## How it works\n\nJupyterHub's REST API is used to acquire information about activity, and if the\nidle culler service based on configuration thinks a server should be stopped or\ndeleted it also does so via JupyterHub's REST API.\n\n### In depth\n\n`jupyterhub-idle-culler` relies on permission to work against JupyterHub's REST\nAPI is provided via the `JUPYTERHUB_API_TOKEN`, that is set automatically for\n[managed services] started by JupyterHub.\n\n`jupyterhub-idle-culler` lists available users and their server's reported\n`last_activity` via JupyterHub's [`/users`] REST API and makes decisions based on\nthat. User's default servers can be stopped via [`/users/{name}/server`], named\nservers can be stopped and optionally removed via\n[`/users/{name}/servers/{server_name}`], and users can optionally be deleted via\n[`/users/{name}`].\n\n[managed services]: https://jupyterhub.readthedocs.io/en/stable/reference/services.html#launching-a-hub-managed-service\n[`/users`]: https://jupyterhub.readthedocs.io/en/stable/reference/rest-api.html#/default/get_users\n[`/users/{name}/server`]: https://jupyterhub.readthedocs.io/en/stable/reference/rest-api.html#/default/delete_users__name__server\n[`/users/{name}/servers/{server_name}`]: https://jupyterhub.readthedocs.io/en/stable/reference/rest-api/index.html#operation--users--name--servers--server_name--delete\n[`/users/{name}`]: https://jupyterhub.readthedocs.io/en/stable/reference/rest-api.html#/default/delete_users__name_\n\nJupyterHub's reported `last_activity` for user servers is updated by JupyterHub\nat a regular interval in the [`update_last_activity` function] that relies on\ntwo sources of information.\n\n[`update_last_activity` function]: https://github.com/jupyterhub/jupyterhub/blob/3.1.1/jupyterhub/app.py#L3002\n\n1. **The proxy's routes data**\n\n   The configurable proxy class for JupyterHub is an interface for JupyterHub to\n   request routing of network traffic to user servers. Through this interface,\n   JupyterHub be informed on network activity if the proxy class provides it,\n   specifically via the [`get_all_routes`] function.\n\n   The [configurable-http-proxy] used in https://z2jh.jupyter.org provides\n   information about network routes activity, but [traefik-proxy] used in\n   https://tljh.jupyter.org [currently does not].\n\n   [`get_all_routes`]: https://jupyterhub.readthedocs.io/en/stable/reference/proxy.html#retrieving-routes\n   [configurable-http-proxy]: https://github.com/jupyterhub/configurable-http-proxy#readme\n   [traefik-proxy]: https://github.com/jupyterhub/traefik-proxy#readme\n   [currently does not]: https://github.com/jupyterhub/traefik-proxy/issues/151\n\n2. **The user server's activity reports**\n\n   The `update_last_activity` function also reads JupyterHub's database that\n   keeps state about servers `last_activity`. These database records are updated\n   whenever a server notifies JupyterHub about activity, as they are required to\n   do.\n\n   Servers has before JupyterHub 4 notified JupyterHub about activity by being\n   started by the [`jupyterhub-singleuser`] script made available by installing\n   `jupyterhub` (or `jupyterhub-singleuser` on conda-forge). With JupyterHub 4+\n   and jupyter_server 2+ a jupyter_server server extension can be used instead.\n\n   The `jupyterhub-singleuser` script launches a modified server application\n   that keeps JupyterHub updated with the server activity via the\n   [`notify_activity`] function.\n\n   The `notify_activity` function in turn make use of the server applications\n   `last_activity` function (see implementation in [NotebookApp] and [ServerApp]\n   respectively) that that combines information from API activity, kernel\n   activity, kernel shutdown, and terminal activity. This activity also covers\n   activity of applications like RStudio running via `jupyter-server-proxy`.\n\n   [`jupyterhub-singleuser`]: https://github.com/jupyterhub/jupyterhub/blob/3.1.1/setup.py#L112\n   [`notify_activity`]: https://github.com/jupyterhub/jupyterhub/blob/3.1.1/jupyterhub/singleuser/mixins.py#L532\n   [notebookapp]: https://github.com/jupyter/notebook/blob/v6.5.2/notebook/notebookapp.py#L391-L396\n   [serverapp]: https://github.com/jupyter-server/jupyter_server/blob/v1.23.5/jupyter_server/serverapp.py#L446-L451\n\nHere is a summary of what's described so far:\n\n1. `jupyterhub-idle-culler` collects information and acts entirely through\n   JupyterHub's REST API.\n2. `jupyterhub-idle-culler` makes decisions based on information provided by\n   JupyterHub, that collects activity reports from the user servers and polls\n   the proxy class for information about user servers' network activity.\n\nNow, as the server's kernel activity influence the activity that servers will\nnotify JupyterHub about, the kernel activity in turn influences\n`jupyterhub-idle-culler`. Due to this, it can be relevant to also learn a little\nabout a mechanism to _cull idle kernels_ as well even though\n`jupyterhub-idle-culler` isn't involved in that.\n\nThe default kernel manager, the `MappingKernelManager`, can be configured to\ncull idle kernels. Its configuration is documented in\n[ServerApp's](https://jupyter-server.readthedocs.io/en/stable/other/full-config.html)\nand\n[NotebookApp's](https://jupyter-notebook.readthedocs.io/en/stable/config.html#options)\nrespective documentation, and here are some relevant kernel culling\nconfiguration options:\n\n- `MappingKernelManager.cull_busy`\n- `MappingKernelManager.cull_idle_timeout`\n- `MappingKernelManager.cull_interval`\n- `MappingKernelManager.cull_connected`\n\n  Note that `cull_connected` can be tricky to understand for JupyterLab as a\n  browser having a web-socket connection to a kernel or not isn't as obvious as\n  it was in the classical Jupyter notebook UI. See [this issue for more\n  details](https://github.com/jupyterlab/jupyterlab/issues/6893).\n\n  Also note that configuration of MappingKernelManager should be made on the\n  user server itself, for example via a `jupyter_server_config.py` file in\n  `/etc/jupyter` or `/usr/local/etc/jupyter` rather than where JupyterHub is\n  running.\n\nFinally, note that a Jupyter server can shut itself down without intervention by\n`jupyterhub-idle-culler` if `ServerApp.shutdown_no_activity_timeout` is\nconfigured.\n\n### Caveats\n\n#### Pagination\n\nJupyterHub 2.0 introduces pagination to the [`/users`] API endpoint. This\npagination does not guarantee a consistent snapshot for consecutive requests\nspread over time, so it is possible for a highly active hub to occasionally miss\nculling users crossing page boundaries between requests. This is expected to be\nan infrequent occurrence and only result in delaying a server being culled by\none cull interval in realistic scenarios, so of minor consequence in JupyterHub.\n\nThe issue can be mitigated by requesting a larger page size, via e.g.\n`--api-page-size=200`, but feel free to open an issue if this is causing a\nproblem for you.\n",
    "bugtrack_url": null,
    "license": "# The Jupyter multi-user notebook server licensing terms  Jupyter multi-user notebook server is licensed under the terms of the Modified BSD License (also known as New or Revised or 3-Clause BSD), as follows:  - Copyright (c) 2014-, Jupyter Development Team  All rights reserved.  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.  Neither the name of the Jupyter Development Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  ## About the Jupyter Development Team  The Jupyter Development Team is the set of all contributors to the Jupyter project. This includes all of the Jupyter subprojects.  The core team that coordinates development on GitHub can be found here: https://github.com/jupyter/.  ## Our Copyright Policy  Jupyter uses a shared copyright model. Each contributor maintains copyright over their contributions to Jupyter. But, it is important to note that these contributions are typically only changes to the repositories. Thus, the Jupyter source code, in its entirety is not the copyright of any single person or institution.  Instead, it is the collective copyright of the entire Jupyter Development Team.  If individual contributors want to maintain a record of what changes/contributions they have specific copyright on, they should indicate their copyright in the commit message of the change, when they commit the change to one of the Jupyter repositories.  With this in mind, the following banner should be used in any source code file to indicate the copyright and license terms:  # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License.",
    "summary": "JupyterHub Python repository template",
    "version": "1.3.0",
    "project_urls": {
        "Documentation": "https://github.com/jupyterhub/jupyterhub-idle-culler#readme",
        "Issues": "https://github.com/jupyterhub/jupyterhub-idle-culler/issues",
        "Source": "https://github.com/jupyterhub/jupyterhub-idle-culler"
    },
    "split_keywords": [
        "jupyterhub"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "29a39bef0540f24440096c8cbcdfcad4f39ab8d4bc899580dc1aeed6c542d426",
                "md5": "e6dbe4f2d496ed481907c49418b605a5",
                "sha256": "efb1d81303b0318f52f4ae5eb13ddbc469b3374f317271ea9815b5cd76d0270a"
            },
            "downloads": -1,
            "filename": "jupyterhub_idle_culler-1.3.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "e6dbe4f2d496ed481907c49418b605a5",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 14973,
            "upload_time": "2024-02-26T08:12:02",
            "upload_time_iso_8601": "2024-02-26T08:12:02.176148Z",
            "url": "https://files.pythonhosted.org/packages/29/a3/9bef0540f24440096c8cbcdfcad4f39ab8d4bc899580dc1aeed6c542d426/jupyterhub_idle_culler-1.3.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d0999a063b360d8f641435c0559a74b08c710ea2e9fff224e1ac0b676449b178",
                "md5": "44fb2f7451b0a434e693f89d255cb521",
                "sha256": "49a22842c82bb861264fb65e3bfcd183488b7eeec5a50915bee8dd7cd9ff2d40"
            },
            "downloads": -1,
            "filename": "jupyterhub_idle_culler-1.3.0.tar.gz",
            "has_sig": false,
            "md5_digest": "44fb2f7451b0a434e693f89d255cb521",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 20302,
            "upload_time": "2024-02-26T08:12:03",
            "upload_time_iso_8601": "2024-02-26T08:12:03.517113Z",
            "url": "https://files.pythonhosted.org/packages/d0/99/9a063b360d8f641435c0559a74b08c710ea2e9fff224e1ac0b676449b178/jupyterhub_idle_culler-1.3.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-02-26 08:12:03",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "jupyterhub",
    "github_project": "jupyterhub-idle-culler#readme",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "jupyterhub-idle-culler"
}
        
Elapsed time: 0.18792s