tigris-snapshots-forking

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Tigris 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

快速参考

OperationFunctionKey Parameters
Create snapshot
createBucketSnapshot(options)
name, sourceBucketName
List snapshots
listBucketSnapshots(sourceBucketName)
sourceBucketName
Create fork
createBucket(name, options)
sourceBucketName, sourceBucketSnapshot
操作功能描述关键参数
创建快照
createBucketSnapshot(options)
name, sourceBucketName
列出快照
listBucketSnapshots(sourceBucketName)
sourceBucketName
创建分支
createBucket(name, options)
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
enableSnapshot: true
when created.
typescript
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: true

List 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 freely
typescript
// 创建生产数据的快照
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

常见错误

MistakeFix
Snapshotting non-snapshot-enabled bucketRecreate bucket with
enableSnapshot: true
Expecting fork to affect sourceForks are isolated - source remains unchanged
Not naming snapshotsNames make snapshots discoverable
Using wrong storage tierSnapshot buckets must use STANDARD tier
错误操作解决方法
为未启用快照的存储桶创建快照重新创建存储桶并设置
enableSnapshot: true
认为分支操作会影响源存储桶分支是隔离的——源存储桶不会发生变化
不为快照命名命名可提高快照的可发现性
使用错误的存储层级快照存储桶必须使用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:
  1. Install @tigrisdata/storage (see installing-tigris-storage)
  2. Create bucket with
    enableSnapshot: true
    (see tigris-bucket-management)
在使用快照/分支前:
  1. 安装@tigrisdata/storage(参见installing-tigris-storage
  2. 创建存储桶时设置
    enableSnapshot: true
    (参见tigris-bucket-management