From 51d328fe6764a17e58ca95eae9d4d3ca255845c6 Mon Sep 17 00:00:00 2001 From: Vasek Mlejnsky Date: Mon, 16 Dec 2024 19:03:24 -0800 Subject: [PATCH] Add docs about persistence --- apps/web/src/app/(docs)/docs/page.mdx | 2 - .../(docs)/docs/sandbox/persistence/page.mdx | 147 +++++++++++++++++- apps/web/src/components/Concepts.tsx | 15 +- .../components/Navigation/NavigationLink.tsx | 13 +- apps/web/src/components/Navigation/routes.tsx | 15 +- apps/web/src/components/Tag.tsx | 59 +------ 6 files changed, 173 insertions(+), 78 deletions(-) diff --git a/apps/web/src/app/(docs)/docs/page.mdx b/apps/web/src/app/(docs)/docs/page.mdx index b0fef3d9f..968045e2d 100644 --- a/apps/web/src/app/(docs)/docs/page.mdx +++ b/apps/web/src/app/(docs)/docs/page.mdx @@ -31,14 +31,12 @@ Some of the typical use cases for E2B are AI data analysis or visualization, run The E2B Sandbox is a small isolated VM the can be started very quickly (~150ms). You can think of it as a small computer for the AI model. You can run many sandboxes at once. Typically, you run separate sandbox for each LLM, user, or AI agent session in your app. For example, if you were building an AI data analysis chatbot, you would start the sandbox for every user session. - ## Quickstart ## Code interpreting with AI - ## Learn the core concepts diff --git a/apps/web/src/app/(docs)/docs/sandbox/persistence/page.mdx b/apps/web/src/app/(docs)/docs/sandbox/persistence/page.mdx index bde9784e1..a027699e2 100644 --- a/apps/web/src/app/(docs)/docs/sandbox/persistence/page.mdx +++ b/apps/web/src/app/(docs)/docs/sandbox/persistence/page.mdx @@ -1,16 +1,149 @@ # Sandbox persistence -We're working on a feature that will allow you to persist sandboxes between runs. + +Sandbox persistence is currently in beta: +1. You need to install the [beta version of the SDKs](#installing-the-beta-version-of-the-sdks) +1. Consider [some limitations](#limitations-while-in-beta). +1. The persistence is free for all users during the beta. + -In the meantime, you can mount cloud storage like Amazon's S3, Google Cloud Storage, or Cloudflare's R2 to the sandbox's filesystem. +The sandbox persistence allows you to pause your sandbox and resume it later from the same state it was in when you paused it. -**Prerequisites** -- Famil +This includes not only state of the sandbox's filesystem but also the sandbox's memory. This means all running processes, loaded variables, data, etc. +## 1. Installing the beta version of the SDKs +To use the sandbox persistence, you need to install the beta version of the SDKs. + +```bash {{ language: 'js' }} +npm i e2b@beta +``` -## Amazon S3 +```bash {{ language: 'python' }} -## Google Cloud Storage +# Install the latest beta version of the SDK on PyPi +# https://pypi.org/project/e2b/#history +pip install e2b-code-interpreter==1.1.0.b17 +``` + -## Cloudflare R2 + +## 2. Pausing sandbox +When you pause a sandbox, both the sandbox's filesystem and memory state will be saved. This includes all the files in the sandbox's filesystem and all the running processes, loaded variables, data, etc. + + +```js +import { Sandbox } from 'e2b' +// or use Code Interpreter: https://github.com/e2b-dev/code-interpreter +// import { Sandbox } from '@e2b/code-interpreter' +// +// or use Desktop: https://github.com/e2b-dev/desktop +// import { Sandbox } from '@e2b/desktop' + +const sbx = await Sandbox.create() +console.log('Sandbox created', sbx.sandboxId) + +// Pause the sandbox +// You can save the sandbox ID in your database +// to resume the sandbox later +const sandboxId = await sbx.pause() // $HighlightLine +console.log('Sandbox paused', sandboxId) // $HighlightLine +``` +```python +from e2b import Sandbox +# or use Code Interpreter: https://github.com/e2b-dev/code-interpreter +# from e2b_code_interpreter import Sandbox +# +# or use Desktop: https://github.com/e2b-dev/desktop +# from e2b_desktop import Sandbox + +sbx = Sandbox() +print('Sandbox created', sbx.sandbox_id) + +# Pause the sandbox +# You can save the sandbox ID in your database +# to resume the sandbox later +sandbox_id = sbx.pause() # $HighlightLine +print('Sandbox paused', sandbox_id) # $HighlightLine +``` + + + +## 3. Resuming sandbox +When you resume a sandbox, it will be in the same state it was in when you paused it. +This means that all the files in the sandbox's filesystem will be restored and all the running processes, loaded variables, data, etc. will be restored. + + +```js +import { Sandbox } from 'e2b' +// or use Code Interpreter: https://github.com/e2b-dev/code-interpreter +// import { Sandbox } from '@e2b/code-interpreter' +// +// or use Desktop: https://github.com/e2b-dev/desktop +// import { Sandbox } from '@e2b/desktop' + +const sbx = await Sandbox.create() +console.log('Sandbox created', sbx.sandboxId) + +// Pause the sandbox +// You can save the sandbox ID in your database +// to resume the sandbox later +const sandboxId = await sbx.pause() +console.log('Sandbox paused', sandboxId) + +// Resume the sandbox from the same state +const sameSbx = await Sandbox.resume(sandboxId) // $HighlightLine +console.log('Sandbox resumed', sameSbx.sandboxId) // $HighlightLine +``` +```python +from e2b import Sandbox +# or use Code Interpreter: https://github.com/e2b-dev/code-interpreter +# from e2b_code_interpreter import Sandbox +# +# or use Desktop: https://github.com/e2b-dev/desktop +# from e2b_desktop import Sandbox + +sbx = Sandbox() +print('Sandbox created', sbx.sandbox_id) + +# Pause the sandbox +# You can save the sandbox ID in your database +# to resume the sandbox later +sandbox_id = sbx.pause() +print('Sandbox paused', sandbox_id) + +# Resume the sandbox from the same state +same_sbx = Sandbox.resume(sandbox_id) # $HighlightLine +print('Sandbox resumed', same_sbx.sandbox_id) # $HighlightLine +``` + + +## Sandbox's timeout +When you resume a sandbox, the sandbox's timeout is reset to the default timeout of an E2B sandbox - 5 minutes. + + +You can pass a custom timeout to the `Sandbox.resume()` method like this: + + +```js +import { Sandbox } from 'e2b' + +const sbx = await Sandbox.resume(sandboxId, { timeoutMs: 60 * 1000 }) // 60 seconds +``` +```python +from e2b import Sandbox + +sbx = Sandbox.resume(sandbox_id, timeout=60) # 60 seconds +``` + + +## Network +If you have a service (for example a server) running inside your sandbox and you pause the sandbox, the service won't be accessible from the outside and all the clients will be disconnected. +If you resume the sandbox, the service will be accessible again but you need to connect clients again. + + +## Limitations while in beta +- It takes about 4 seconds per 1 GB RAM to pause the sandbox +- It takes about 2 seconds to resume the sandbox + - Soon, this will get to the same speed as calling `Sandbox.create()` +- Sandbox can be paused up to 30 days \ No newline at end of file diff --git a/apps/web/src/components/Concepts.tsx b/apps/web/src/components/Concepts.tsx index 238633368..e8d475d20 100644 --- a/apps/web/src/components/Concepts.tsx +++ b/apps/web/src/components/Concepts.tsx @@ -1,7 +1,8 @@ import { FolderTree, Terminal, - Hourglass + Hourglass, + RefreshCcw, } from 'lucide-react' import { @@ -16,6 +17,12 @@ const concepts: BoxItem[] = [ description: 'Learn about how to start the sandbox, manage its lifecycle, and interact with it.', icon: , }, + { + href: '/docs/sandbox/persistence', + title: 'Sandbox persistence', + description: 'Learn how to achieve data persistence by pausing and resuming sandboxes.', + icon: , + }, // { // href: '/docs/code-execution', // title: 'AI code execution', @@ -25,17 +32,17 @@ const concepts: BoxItem[] = [ { href: '/docs/filesystem', title: 'Filesystem', - description: 'Each sandbox has its own isolated filesystem that you can use to create, read, write, and delete files.', + description: 'Sandbox has an isolated filesystem that you can use to create, read, write, and delete files.', icon: , }, { href: '/docs/commands', title: 'Commands', - description: 'You can run terminal commands inside the Sandbox. This allows you to start any process inside the Sandbox.', + description: 'Run terminal commands inside the Sandbox and start any process inside the Sandbox.', icon: , } ] export function Concepts() { return -} +} \ No newline at end of file diff --git a/apps/web/src/components/Navigation/NavigationLink.tsx b/apps/web/src/components/Navigation/NavigationLink.tsx index 5153a35a9..db85b6c6e 100644 --- a/apps/web/src/components/Navigation/NavigationLink.tsx +++ b/apps/web/src/components/Navigation/NavigationLink.tsx @@ -8,12 +8,10 @@ import { NavLink } from './routes' export function NavigationLink({ className, link, - tag, }: { className?: string link: NavLink - tag?: string }) { const pathname = usePathname() // Add this to get the hash @@ -41,17 +39,14 @@ export function NavigationLink({ >
{link.icon} - {tag ? ( + {link.tag ? (
- - {tag} - {link.title} + + {link.tag} +
) : ( diff --git a/apps/web/src/components/Navigation/routes.tsx b/apps/web/src/components/Navigation/routes.tsx index 466fe5f9a..d1bf3ea30 100644 --- a/apps/web/src/components/Navigation/routes.tsx +++ b/apps/web/src/components/Navigation/routes.tsx @@ -1,9 +1,14 @@ import { Braces, CheckCircle, Home, MessagesSquare } from 'lucide-react' import sdkRefRoutesJson from './sdkRefRoutes.json' +enum Tag { + New = 'New', +} + export interface NavLink { title: string href: string + tag?: Tag icon?: React.ReactNode } @@ -270,6 +275,11 @@ export const docRoutes: NavGroup[] = [ title: 'Lifecycle', href: '/docs/sandbox', }, + { + title: 'Persistence', + tag: Tag.New, + href: '/docs/sandbox/persistence', + }, { title: 'Metadata', href: '/docs/sandbox/metadata', @@ -295,9 +305,6 @@ export const docRoutes: NavGroup[] = [ // href: '/docs/sandbox/request-timeouts', // }, // { - // title: '* Persistence', - // href: '/docs/sandbox/persistence', - // }, ], }, { @@ -479,4 +486,4 @@ export const sdkRefRoutes: VersionedNavGroup[] = ( group?.title && sdkRefNameMap[group.title] ? sdkRefNameMap[group.title] : group.title, - })) + })) \ No newline at end of file diff --git a/apps/web/src/components/Tag.tsx b/apps/web/src/components/Tag.tsx index 1d17fd30a..a4184b050 100644 --- a/apps/web/src/components/Tag.tsx +++ b/apps/web/src/components/Tag.tsx @@ -1,64 +1,19 @@ import clsx from 'clsx' - -const variantStyles = { - small: '', - medium: 'rounded-lg px-1.5 ring-1 ring-inset', -} - -const colorStyles = { - emerald: { - small: 'text-brand-500 dark:text-brand-400', - medium: - 'ring-brand-300 dark:ring-brand-400/30 bg-brand-400/10 text-brand-500 dark:text-brand-400', - }, - sky: { - small: 'text-sky-500', - medium: - 'ring-sky-300 bg-sky-400/10 text-sky-500 dark:ring-sky-400/30 dark:bg-sky-400/10 dark:text-sky-400', - }, - amber: { - small: 'text-amber-500', - medium: - 'ring-amber-300 bg-amber-400/10 text-amber-500 dark:ring-amber-400/30 dark:bg-amber-400/10 dark:text-amber-400', - }, - rose: { - small: 'text-red-500 dark:text-rose-500', - medium: - 'ring-rose-200 bg-rose-50 text-red-500 dark:ring-rose-500/20 dark:bg-rose-400/10 dark:text-rose-400', - }, - zinc: { - small: 'text-zinc-400 dark:text-zinc-500', - medium: - 'ring-zinc-200 bg-zinc-50 text-zinc-500 dark:ring-zinc-500/20 dark:bg-zinc-400/10 dark:text-zinc-400', - }, -} - -const valueColorMap = { - GET: 'emerald', - POST: 'sky', - PUT: 'amber', - DELETE: 'rose', -} as Record +import React from 'react' export function Tag({ - children, - variant = 'medium', - color = valueColorMap[children] ?? 'emerald', - }: { - // eslint-disable-next-line @typescript-eslint/ban-types - children: keyof typeof valueColorMap & (string | {}) - variant?: keyof typeof variantStyles - color?: keyof typeof colorStyles + children, +}: { + children: React.ReactNode }) { return ( {children} ) -} +} \ No newline at end of file