Skip to content

Commit 9204d86

Browse files
Add guide on cleaning up state and file resources (#8610)
* WIP * Add guide * add changeset * notebook * No log in --------- Co-authored-by: gradio-pr-bot <[email protected]>
1 parent affce4c commit 9204d86

File tree

7 files changed

+61
-8
lines changed

7 files changed

+61
-8
lines changed

.changeset/smart-rooms-win.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gradio": patch
3+
---
4+
5+
fix:Add guide on cleaning up state and file resources

demo/state_cleanup/run.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: state_cleanup"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["from __future__ import annotations\n", "import gradio as gr\n", "import numpy as np\n", "from PIL import Image\n", "from pathlib import Path\n", "import secrets\n", "import shutil\n", "\n", "current_dir = Path(__file__).parent\n", "\n", "\n", "def generate_random_img(history: list[Image.Image], request: gr.Request):\n", " \"\"\"Generate a random red, green, blue, orange, yellor or purple image.\"\"\"\n", " colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 165, 0), (255, 255, 0), (128, 0, 128)]\n", " color = colors[np.random.randint(0, len(colors))]\n", " img = Image.new('RGB', (100, 100), color)\n", " \n", " user_dir: Path = current_dir / request.username # type: ignore\n", " user_dir.mkdir(exist_ok=True)\n", " path = user_dir / f\"{secrets.token_urlsafe(8)}.webp\"\n", "\n", " img.save(path)\n", " history.append(img)\n", "\n", " return img, history, history\n", "\n", "def delete_directory(req: gr.Request):\n", " if not req.username:\n", " return\n", " user_dir: Path = current_dir / req.username\n", " shutil.rmtree(str(user_dir))\n", "\n", "with gr.Blocks() as demo:\n", " gr.Markdown(\"\"\"# State Cleanup Demo\n", " \ud83d\uddbc\ufe0f Images are saved in a user-specific directory and deleted when the users closes the page via demo.unload.\n", " \"\"\")\n", " with gr.Row():\n", " with gr.Column(scale=1):\n", " with gr.Row():\n", " img = gr.Image(label=\"Generated Image\", height=300, width=300)\n", " with gr.Row():\n", " gen = gr.Button(value=\"Generate\")\n", " with gr.Row():\n", " history = gr.Gallery(label=\"Previous Generations\", height=500, columns=10)\n", " state = gr.State(value=[], delete_callback=lambda v: print(\"STATE DELETED\"))\n", "\n", " demo.load(generate_random_img, [state], [img, state, history]) \n", " gen.click(generate_random_img, [state], [img, state, history])\n", " demo.unload(delete_directory)\n", "\n", "\n", "demo.launch(auth=lambda user,pwd: True,\n", " auth_message=\"Enter any username and password to continue\")"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
1+
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: state_cleanup"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["from __future__ import annotations\n", "import gradio as gr\n", "import numpy as np\n", "from PIL import Image\n", "from pathlib import Path\n", "import secrets\n", "import shutil\n", "\n", "current_dir = Path(__file__).parent\n", "\n", "\n", "def generate_random_img(history: list[Image.Image], request: gr.Request):\n", " \"\"\"Generate a random red, green, blue, orange, yellor or purple image.\"\"\"\n", " colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 165, 0), (255, 255, 0), (128, 0, 128)]\n", " color = colors[np.random.randint(0, len(colors))]\n", " img = Image.new('RGB', (100, 100), color)\n", "\n", " user_dir: Path = current_dir / request.session_hash\n", " user_dir.mkdir(exist_ok=True)\n", " path = user_dir / f\"{secrets.token_urlsafe(8)}.webp\"\n", "\n", " img.save(path)\n", " history.append(img)\n", "\n", " return img, history, history\n", "\n", "def delete_directory(req: gr.Request):\n", " if not req.username:\n", " return\n", " user_dir: Path = current_dir / req.username\n", " shutil.rmtree(str(user_dir))\n", "\n", "with gr.Blocks(delete_cache=(60, 3600)) as demo:\n", " gr.Markdown(\"\"\"# State Cleanup Demo\n", " \ud83d\uddbc\ufe0f Images are saved in a user-specific directory and deleted when the users closes the page via demo.unload.\n", " \"\"\")\n", " with gr.Row():\n", " with gr.Column(scale=1):\n", " with gr.Row():\n", " img = gr.Image(label=\"Generated Image\", height=300, width=300)\n", " with gr.Row():\n", " gen = gr.Button(value=\"Generate\")\n", " with gr.Row():\n", " history = gr.Gallery(label=\"Previous Generations\", height=500, columns=10)\n", " state = gr.State(value=[], delete_callback=lambda v: print(\"STATE DELETED\"))\n", "\n", " demo.load(generate_random_img, [state], [img, state, history])\n", " gen.click(generate_random_img, [state], [img, state, history])\n", " demo.unload(delete_directory)\n", "\n", "\n", "demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

demo/state_cleanup/run.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ def generate_random_img(history: list[Image.Image], request: gr.Request):
1414
colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 165, 0), (255, 255, 0), (128, 0, 128)]
1515
color = colors[np.random.randint(0, len(colors))]
1616
img = Image.new('RGB', (100, 100), color)
17-
18-
user_dir: Path = current_dir / request.username # type: ignore
17+
18+
user_dir: Path = current_dir / request.session_hash
1919
user_dir.mkdir(exist_ok=True)
2020
path = user_dir / f"{secrets.token_urlsafe(8)}.webp"
2121

@@ -30,7 +30,7 @@ def delete_directory(req: gr.Request):
3030
user_dir: Path = current_dir / req.username
3131
shutil.rmtree(str(user_dir))
3232

33-
with gr.Blocks() as demo:
33+
with gr.Blocks(delete_cache=(60, 3600)) as demo:
3434
gr.Markdown("""# State Cleanup Demo
3535
🖼️ Images are saved in a user-specific directory and deleted when the users closes the page via demo.unload.
3636
""")
@@ -44,10 +44,9 @@ def delete_directory(req: gr.Request):
4444
history = gr.Gallery(label="Previous Generations", height=500, columns=10)
4545
state = gr.State(value=[], delete_callback=lambda v: print("STATE DELETED"))
4646

47-
demo.load(generate_random_img, [state], [img, state, history])
47+
demo.load(generate_random_img, [state], [img, state, history])
4848
gen.click(generate_random_img, [state], [img, state, history])
4949
demo.unload(delete_directory)
5050

5151

52-
demo.launch(auth=lambda user,pwd: True,
53-
auth_message="Enter any username and password to continue")
52+
demo.launch()

gradio/blocks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def __init__(
131131
self.temp_files: set[str] = set()
132132
self.GRADIO_CACHE = get_upload_folder()
133133
self.key = key
134-
# Keep tracks of files that should not be deleted when the delete_cache parmaeter is set
134+
# Keep tracks of files that should not be deleted when the delete_cache parmameter is set
135135
# These files are the default value of the component and files that are used in examples
136136
self.keep_in_cache = set()
137137

gradio/components/state.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class State(Component):
1818
"""
1919
Special hidden component that stores session state across runs of the demo by the
2020
same user. The value of the State variable is cleared when the user refreshes the page.
21+
The state state is stored on the server for 60 minutes after the user closes the tab.
2122
Demos: interface_state, blocks_simple_squares, state_cleanup
2223
Guides: real-time-speech-recognition
2324
"""
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Resource Cleanup
2+
3+
Your Gradio application may create resources during its lifetime.
4+
Examples of resources are `gr.State` variables, any variables you create and explicitly hold in memory, or files you save to disk.
5+
Over time, these resources can use up all of your server's RAM or disk space and crash your application.
6+
7+
Gradio provides some tools for you to clean up the resources created by your app:
8+
9+
1. Automatic deletion of `gr.State` variables.
10+
2. Automatic cache cleanup with the `delete_cache` parameter.
11+
2. The `Blocks.unload` event.
12+
13+
Let's take a look at each of them individually.
14+
15+
## Automatic deletion of `gr.State`
16+
17+
When a user closes their browser tab, Gradio will automatically delete any `gr.State` variables associated with that user session after 60 minutes. If the user connects again within those 60 minutes, no state will be deleted.
18+
19+
You can control the deletion behavior further with the following two parameters of `gr.State`:
20+
21+
1. `delete_callback` - An arbitrary function that will be called when the variable is deleted. This function must take the state value as input. This function is useful for deleting variables from GPU memory.
22+
2. `time_to_live` - The number of seconds the state should be stored for after it is created or updated. This will delete variables before the session is closed, so it's useful for clearing state for potentially long running sessions.
23+
24+
## Automatic cache cleanup via `delete_cache`
25+
26+
Your Gradio application will save uploaded and generated files to a special directory called the cache directory. Gradio uses a hashing scheme to ensure that duplicate files are not saved to the cache but over time the size of the cache will grow (especially if your app goes viral 😉).
27+
28+
Gradio can periodically clean up the cache for you if you specify the `delete_cache` parameter of `gr.Blocks()`, `gr.Interface()`, or `gr.ChatInterface()`.
29+
This parameter is a tuple of the form `[frequency, age]` both expressed in number of seconds.
30+
Every `frequency` seconds, the temporary files created by this Blocks instance will be deleted if more than `age` seconds have passed since the file was created.
31+
For example, setting this to (86400, 86400) will delete temporary files every day if they are older than a day old.
32+
Additionally, the cache will be deleted entirely when the server restarts.
33+
34+
## The `unload` event
35+
36+
Additionally, Gradio now includes a `Blocks.unload()` event, allowing you to run arbitrary cleanup functions when users disconnect (this does not have a 60 minute delay).
37+
Unlike other gradio events, this event does not accept inputs or outptus.
38+
You can think of the `unload` event as the opposite of the `load` event.
39+
40+
## Putting it all together
41+
42+
The following demo uses all of these features. When a user visits the page, a special unique directory is created for that user.
43+
As the user interacts with the app, images are saved to disk in that special directory.
44+
When the user closes the page, the images created in that session are deleted via the `unload` event.
45+
The state and files in the cache are cleaned up automatically as well.
46+
47+
$code_state_cleanup
48+
$demo_state_cleanup

0 commit comments

Comments
 (0)