tigris-snapshots-forking
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTigris Snapshots and Forking
Tigris 快照与分支(Forking)
Overview
概述
Snapshots capture your entire bucket at a point in time. Forking creates instant, isolated copies from snapshots using copy-on-write.
Core principle: Snapshots and forks protect your data from deletion. Even if you delete everything in a fork, the source bucket data remains intact.
**快照(Snapshots)**会在某个时间点捕获您的整个存储桶状态。**分支(Forking)**利用写时复制(copy-on-write)技术,从快照创建即时、隔离的副本。
**核心原则:**快照和分支可保护您的数据免遭删除。即使您删除分支中的所有内容,源存储桶的数据也会保持完整。
Why Snapshots Matter
快照的重要性
Object storage serves as the primary data store for many systems. It needs safety features:
- Point-in-time recovery - Restore after accidental deletion or corruption
- Version control - Tag meaningful states like releases
- Reproducibility - Recreate exact environments for debugging or testing
- Deletion protection - Forks can be destroyed without affecting source
Traditional object versioning only works per-object. To restore a bucket to a point in time, you must check and restore each object individually. Tigris snapshots capture the entire bucket state instantly.
对象存储是许多系统的主要数据存储介质,它需要安全特性:
- 时间点恢复 - 在意外删除或数据损坏后恢复数据
- 版本控制 - 为发布等重要状态添加标签
- 可复现性 - 重建完全一致的环境用于调试或测试
- 删除保护 - 分支可被销毁而不影响源数据
传统的对象版本控制仅针对单个对象生效。要将存储桶恢复到某个时间点,您必须逐个检查并恢复每个对象。Tigris快照可即时捕获整个存储桶的状态。
Why Forking Matters
分支的重要性
Forking creates isolated bucket copies instantly - even for terabytes of data:
- Developer sandboxes - Test with real production data safely
- AI agent environments - Spin up agents with pre-loaded dependencies
- Load testing - Use production data without risk
- Feature branch testing - Parallel environments for experiments
- Training experiments - Fork datasets to test without affecting source
How it works: Tigris uses immutable objects with backwards-ordered timestamps. Forks read from the parent snapshot until new data overwrites. This makes forking essentially free - no data copying required.
分支可即时创建隔离的存储桶副本——即使是数TB的数据也能瞬间完成:
- 开发者沙箱 - 安全地使用真实生产数据进行测试
- AI Agent环境 - 快速启动预加载依赖的Agent
- 负载测试 - 使用生产数据而无风险
- 功能分支测试 - 为实验创建并行环境
- 训练实验 - 分支数据集进行测试,不影响源数据
**工作原理:**Tigris使用带有反向时间戳的不可变对象。分支会从父快照读取数据,直到有新数据覆盖它。这使得分支操作几乎没有成本——无需复制任何数据。
Quick Reference
快速参考
| Operation | Function | Key Parameters |
|---|---|---|
| Create snapshot | | name, sourceBucketName |
| List snapshots | | sourceBucketName |
| Create fork | | sourceBucketName, sourceBucketSnapshot |
| 操作 | 功能描述 | 关键参数 |
|---|---|---|
| 创建快照 | | name, sourceBucketName |
| 列出快照 | | sourceBucketName |
| 创建分支 | | sourceBucketName, sourceBucketSnapshot |
Create Snapshot
创建快照
typescript
import { createBucketSnapshot } from "@tigrisdata/storage";
// Snapshot default bucket
const result = await createBucketSnapshot();
if (result.error) {
console.error("Error:", result.error);
} else {
console.log("Snapshot version:", result.data?.snapshotVersion);
// Output: { snapshotVersion: "1751631910169675092" }
}
// Named snapshot for specific bucket
const result = await createBucketSnapshot("my-bucket", {
name: "backup-before-migration",
});
if (result.error) {
console.error("Error:", result.error);
} else {
console.log("Named snapshot:", result.data?.snapshotVersion);
}Prerequisite: Bucket must have when created.
enableSnapshot: truetypescript
import { createBucketSnapshot } from "@tigrisdata/storage";
// 为默认存储桶创建快照
const result = await createBucketSnapshot();
if (result.error) {
console.error("Error:", result.error);
} else {
console.log("Snapshot version:", result.data?.snapshotVersion);
// 输出: { snapshotVersion: "1751631910169675092" }
}
// 为指定存储桶创建命名快照
const result = await createBucketSnapshot("my-bucket", {
name: "backup-before-migration",
});
if (result.error) {
console.error("Error:", result.error);
} else {
console.log("Named snapshot:", result.data?.snapshotVersion);
}**前提条件:**创建存储桶时必须设置。
enableSnapshot: trueList Snapshots
列出快照
typescript
import { listBucketSnapshots } from "@tigrisdata/storage";
// List snapshots for default bucket
const result = await listBucketSnapshots();
if (result.error) {
console.error("Error:", result.error);
} else {
console.log("Snapshots:", result.data);
// [
// {
// name: "backup-before-migration",
// version: "1751631910169675092",
// creationDate: Date("2025-01-15T08:30:00Z")
// }
// ]
}
// List snapshots for specific bucket
const result = await listBucketSnapshots("my-bucket");typescript
import { listBucketSnapshots } from "@tigrisdata/storage";
// 列出默认存储桶的快照
const result = await listBucketSnapshots();
if (result.error) {
console.error("Error:", result.error);
} else {
console.log("Snapshots:", result.data);
// [
// {
// name: "backup-before-migration",
// version: "1751631910169675092",
// creationDate: Date("2025-01-15T08:30:00Z")
// }
// ]
}
// 列出指定存储桶的快照
const result = await listBucketSnapshots("my-bucket");Create Fork from Snapshot
从快照创建分支
typescript
import { createBucket, createBucketSnapshot } from "@tigrisdata/storage";
// First, create a snapshot
const snapshot = await createBucketSnapshot("agent-seed", {
name: "agent-seed-v1",
});
const snapshotVersion = snapshot.data?.snapshotVersion;
// Fork from snapshot
const agentBucketName = `agent-${Date.now()}`;
const forkResult = await createBucket(agentBucketName, {
sourceBucketName: "agent-seed",
sourceBucketSnapshot: snapshotVersion,
});
if (forkResult.error) {
console.error("Error:", forkResult.error);
} else {
console.log("Forked bucket created");
// agent-${timestamp} has all data from agent-seed at snapshot time
// Can modify/delete freely - agent-seed is unaffected
}typescript
import { createBucket, createBucketSnapshot } from "@tigrisdata/storage";
// 首先,创建一个快照
const snapshot = await createBucketSnapshot("agent-seed", {
name: "agent-seed-v1",
});
const snapshotVersion = snapshot.data?.snapshotVersion;
// 从快照创建分支
const agentBucketName = `agent-${Date.now()}`;
const forkResult = await createBucket(agentBucketName, {
sourceBucketName: "agent-seed",
sourceBucketSnapshot: snapshotVersion,
});
if (forkResult.error) {
console.error("Error:", forkResult.error);
} else {
console.log("Forked bucket created");
// agent-${timestamp} 包含快照时 agent-seed 的所有数据
// 可自由修改/删除 - agent-seed 不会受到影响
}Read from Snapshot Version
从快照版本读取数据
Access historical data without forking:
typescript
import { get, list } from "@tigrisdata/storage";
// Get object as it was at snapshot
const result = await get("config.json", "string", {
snapshotVersion: "1751631910169675092",
});
// List objects as they were at snapshot
const result = await list({
snapshotVersion: "1751631910169675092",
});无需创建分支即可访问历史数据:
typescript
import { get, list } from "@tigrisdata/storage";
// 获取快照时间点的对象状态
const result = await get("config.json", "string", {
snapshotVersion: "1751631910169675092",
});
// 列出快照时间点的所有对象
const result = await list({
snapshotVersion: "1751631910169675092",
});Deletion Protection in Action
删除保护的实际效果
typescript
import { remove, get } from "@tigrisdata/storage";
// In forked bucket, delete everything
await remove("hello.txt", { config: { bucket: "agent-fork" } });
// Fork appears empty
const forkResult = await get("hello.txt", "string", {
config: { bucket: "agent-fork" },
});
// forkResult.error === "Not found"
// But source bucket still has data
const sourceResult = await get("hello.txt", "string", {
config: { bucket: "agent-seed" },
});
// sourceResult.data === "Hello, world!"The fork's deletion only affects the fork. Source data remains accessible in the parent bucket and all snapshots.
typescript
import { remove, get } from "@tigrisdata/storage";
// 在分支存储桶中删除所有内容
await remove("hello.txt", { config: { bucket: "agent-fork" } });
// 分支看起来是空的
const forkResult = await get("hello.txt", "string", {
config: { bucket: "agent-fork" },
});
// forkResult.error === "Not found"
// 但源存储桶仍有数据
const sourceResult = await get("hello.txt", "string", {
config: { bucket: "agent-seed" },
});
// sourceResult.data === "Hello, world!"分支中的删除操作仅会影响分支本身。源数据在父存储桶和所有快照中仍然可访问。
Use Cases
使用场景
Developer Sandboxes
开发者沙箱
typescript
// Create snapshot of production data
await createBucketSnapshot("production", {
name: "dev-sandbox-seed",
});
// Fork for each developer
const devBucket = await createBucket(`dev-${developerName}`, {
sourceBucketName: "production",
sourceBucketSnapshot: "...",
});
// Developer can test and modify freelytypescript
// 创建生产数据的快照
await createBucketSnapshot("production", {
name: "dev-sandbox-seed",
});
// 为每位开发者创建分支
const devBucket = await createBucket(`dev-${developerName}`, {
sourceBucketName: "production",
sourceBucketSnapshot: "...",
});
// 开发者可自由测试和修改AI Agent Environments
AI Agent环境
typescript
// Store agent dependencies in seed bucket
await put("model.bin", modelData);
await put("config.json", agentConfig);
// Snapshot the seed
const snapshot = await createBucketSnapshot("agent-seed", {
name: "v1",
});
// Spin up new agent instance with fork
const agentBucket = `agent-${Date.now()}`;
await createBucket(agentBucket, {
sourceBucketName: "agent-seed",
sourceBucketSnapshot: snapshot.data?.snapshotVersion,
});
// Agent has everything and can modify freely
await startAgent(agentBucket);typescript
// 将Agent依赖存储在种子存储桶中
await put("model.bin", modelData);
await put("config.json", agentConfig);
// 为种子存储桶创建快照
const snapshot = await createBucketSnapshot("agent-seed", {
name: "v1",
});
// 通过分支启动新的Agent实例
const agentBucket = `agent-${Date.now()}`;
await createBucket(agentBucket, {
sourceBucketName: "agent-seed",
sourceBucketSnapshot: snapshot.data?.snapshotVersion,
});
// Agent拥有所有依赖,可自由修改
await startAgent(agentBucket);Pre-Migration Backups
迁移前备份
typescript
// Before risky operation
await createBucketSnapshot("production", {
name: "before-migration-v2",
});
// Run migration
// If disaster strikes, fork from snapshot to recover
const rollback = await createBucket("production-restored", {
sourceBucketName: "production",
sourceBucketSnapshot: "...",
});typescript
// 在执行高风险操作前
await createBucketSnapshot("production", {
name: "before-migration-v2",
});
// 执行迁移
// 如果发生故障,从快照创建分支以恢复数据
const rollback = await createBucket("production-restored", {
sourceBucketName: "production",
sourceBucketSnapshot: "...",
});Common Mistakes
常见错误
| Mistake | Fix |
|---|---|
| Snapshotting non-snapshot-enabled bucket | Recreate bucket with |
| Expecting fork to affect source | Forks are isolated - source remains unchanged |
| Not naming snapshots | Names make snapshots discoverable |
| Using wrong storage tier | Snapshot buckets must use STANDARD tier |
| 错误操作 | 解决方法 |
|---|---|
| 为未启用快照的存储桶创建快照 | 重新创建存储桶并设置 |
| 认为分支操作会影响源存储桶 | 分支是隔离的——源存储桶不会发生变化 |
| 不为快照命名 | 命名可提高快照的可发现性 |
| 使用错误的存储层级 | 快照存储桶必须使用STANDARD层级 |
Limitations
局限性
- Existing buckets cannot be snapshot-enabled (must create new bucket)
- Snapshot buckets require STANDARD storage tier
- Snapshot buckets don't support lifecycle transitions or TTL
- 现有存储桶无法启用快照功能(必须创建新存储桶)
- 快照存储桶需要使用STANDARD存储层级
- 快照存储桶不支持生命周期转换或TTL
How It Works (Deep Dive)
深层工作原理
A snapshot is a single 64-bit integer representing nanoseconds since Unix epoch. Tigris stores objects with reverse-ordered timestamps, so the most recent version sorts first. When you snapshot, Tigris records the current time. Reading from a snapshot queries for the newest object version before that timestamp.
Forking adds recursive indirection: child bucket objects override the parent, but missing objects recurse through the parent snapshot. This makes forking instant - no data copying, just metadata pointers.
快照是一个64位整数,代表从Unix纪元开始的纳秒数。Tigris使用带有反向时间戳的不可变对象,因此最新版本会排在最前面。当您创建快照时,Tigris会记录当前时间。从快照读取数据时,会查询该时间戳之前的最新对象版本。
分支操作添加了递归间接层:子存储桶的对象会覆盖父存储桶的对象,但缺失的对象会递归读取父快照中的内容。这使得分支操作瞬间完成——无需复制数据,仅需元数据指针。
Prerequisites
前提条件
Before using snapshots/forking:
- Install @tigrisdata/storage (see installing-tigris-storage)
- Create bucket with (see tigris-bucket-management)
enableSnapshot: true
在使用快照/分支前:
- 安装@tigrisdata/storage(参见installing-tigris-storage)
- 创建存储桶时设置(参见tigris-bucket-management)
enableSnapshot: true