> ## Documentation Index
> Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# LaserStream 历史重播：补充缺失数据

> 通过 LaserStream 的历史重播功能，从断线中恢复并补充缺失的 Solana 区块链数据。再也不会错过任何交易。

<Info>
  **不漏掉任何一步**：LaserStream的历史重播确保您可以从断开连接中恢复，并补充过去24小时的区块链活动中遗漏的数据。
</Info>

## 什么是历史重播？

Historical Replay 是 LaserStream 的一个功能，它允许您通过设置 `fromSlot` 起始点来重播最近的区块链数据。这对于处理断开连接和确保实时应用的数据连续性非常有用。

## 您可以回放的历史长度

无论您订阅哪个承诺级别，您都可以回放长达约 24 小时（约 216,000 个槽）的历史数据。在该时间窗口内传递一个 `fromSlot`，LaserStream 将从那里流式传输到当前时间。

**较早的重播返回已完成的数据。** LaserStream 在内存中保留最后约 20 分钟的槽；更早的数据则从仅持有已完成区块的历史存储中提供。因此，当您回放一个超过约 20 分钟的 `fromSlot` 时，数据反映的是已完成的链——即使在 `processed` 或 `confirmed` 订阅中，您也不会看到分叉或丢弃的槽或槽内账户更新。最近 20 分钟内的槽具有真正的承诺语义，包括分叉和槽内账户更新。

<Warning>
  **时间窗口有限**：历史重播仅涵盖最近约 24 小时。您不能重播过去任意时间点的数据。
</Warning>

<CardGroup cols={2}>
  <Card title="处理断开连接" icon="clock-rotate-left">
    恢复在短暂断开连接期间（最多 24 小时）丢失的数据
  </Card>

  <Card title="引导程序应用" icon="rocket">
    使用最近 24 小时的上下文启动应用程序
  </Card>

  <Card title="分析最近事件" icon="magnifying-glass">
    审查最近的交易和账户变更
  </Card>

  <Card title="使用最近数据进行测试" icon="flask">
    使用真实的最近数据进行测试和开发
  </Card>
</CardGroup>

## 工作原理

<Steps>
  <Step title="指定起始点">
    使用 `fromSlot` 参数设置您的重播起始点（必须在最近约 216,000 个槽内）
  </Step>

  <Step title="流式传输历史数据">
    LaserStream 从您指定的槽开始传送所有事件
  </Step>

  <Step title="赶上实时数据">
    历史数据流式传输直到您到达当前槽
  </Step>

  <Step title="继续实时流式传输">
    无缝过渡到实时数据流
  </Step>
</Steps>

<Note>
  **自动重新连接**: [LaserStream SDK](https://github.com/helius-labs/laserstream-sdk)自动处理重新连接和重放。无需额外代码！
</Note>

## 快速入门

<Tip>
  从您的[Helius 控制台](https://dashboard.helius.dev/laserstream)开始使用 LaserStream。主网需要企业或专业计划；开发网在开发者及以上计划中可用。详细信息请参阅[计划与定价](/docs/zh/billing/plans)。
</Tip>

<Tabs>
  <Tab title="gRPC">
    ```typescript theme={"system"}
    import { subscribe, CommitmentLevel, LaserstreamConfig, SubscribeRequest } from 'helius-laserstream';

    // Pick a slot within the last ~216,000 slots (≈24 h). For a real start
    // value, call `getSlot` first and subtract however far back you want to replay.
    // Note: replays older than ~20 min return finalized data even at processed/confirmed;
    // slots within the last ~20 min carry true commitment semantics.
    const fromSlot = 419_800_000;

    const subscriptionRequest: SubscribeRequest = {
      transactions: {
        "token-filter": { // user-defined label for this filter
          accountInclude: ['TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'],
          vote: false,
          failed: false
        }
      },
      commitment: CommitmentLevel.CONFIRMED,
      accounts: {},
      slots: {},
      blocks: {},
      blocksMeta: {},
      entry: {},
      accountsDataSlice: [],
      fromSlot, // u64 slot number; must fall inside the replay window
    };

    const config: LaserstreamConfig = {
      apiKey: 'YOUR_API_KEY',
      endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com', // Choose your closest region
    };

    await subscribe(config, subscriptionRequest, 
      async (data) => {
        console.log('Received data:', data);
      }, 
      async (error) => {
        console.error('Error:', error);
      }
    );
    ```
  </Tab>
</Tabs>

## 配置选项

<ParamField path="fromSlot" type="number" required>
  从这个槽号开始重放，作为一个`u64`。必须在重放窗口内（最后约216,000个槽 / 当前槽的约24小时内），任何承诺级别均可。

  **示例**: `currentSlot - 1000`

  **重要**: 如果您传递的槽比窗口更旧，LaserStream会拒绝请求并返回`Operation was attempted past the valid range`。
</ParamField>

## 使用案例

<AccordionGroup>
  <Accordion title="短暂断开后的重新连接">
    当您的应用程序在短暂断开连接（少于24小时）后重新连接时，可以使用历史重放确保未遗漏数据。`getCurrentSlot`调用Helius RPC；`lastProcessedSlot`保存在内存中——可根据您的应用需求进行持久化（Redis、Postgres、文件等）。

    ```typescript theme={"system"}
    async function getCurrentSlot(): Promise<number> {
      const r = await fetch(`https://mainnet.helius-rpc.com/?api-key=${process.env.HELIUS_API_KEY}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'getSlot', params: [{ commitment: 'confirmed' }] }),
      });
      const { result } = await r.json();
      return result as number;
    }

    // Load the last slot you processed from wherever you store it.
    let lastProcessedSlot = Number(process.env.LAST_PROCESSED_SLOT ?? 0);

    // Check if it's still within the replay window
    const currentSlot = await getCurrentSlot();
    const maxReplaySlot = currentSlot - 216_000;

    if (lastProcessedSlot < maxReplaySlot) {
      console.warn('Disconnection too long, some data may be lost');
      lastProcessedSlot = maxReplaySlot;
    }

    const subscriptionRequest: SubscribeRequest = {
      // ... your subscription config
      fromSlot: lastProcessedSlot, // pass as number, not string
    };

    await subscribe(config, subscriptionRequest,
      async (data) => {
        // your handler here
        if (data.transaction?.slot) {
          lastProcessedSlot = Number(data.transaction.slot);
          // persist `lastProcessedSlot` here so the next reconnect picks up
        }
      }
    );
    ```
  </Accordion>

  <Accordion title="使用最近上下文启动">
    使用最近几分钟的上下文启动您的应用程序：

    ```typescript theme={"system"}
    // Get a slot from 10 minutes ago (within the 24-hour window)
    const currentSlot = await getCurrentSlot();
    const startSlot = currentSlot - 1500; // ~10 minutes ago

    const subscriptionRequest: SubscribeRequest = {
      // ... your subscription config
      fromSlot: startSlot, // u64 number
    };
    ```
  </Accordion>

  <Accordion title="使用最近数据进行测试">
    使用最近的历史数据进行测试（限最后24小时）：

    ```typescript theme={"system"}
    // Test with data from the last 5 minutes
    const currentSlot = await getCurrentSlot();
    const testStartSlot = currentSlot - 750; // ~5 minutes ago
    const testEndSlot = currentSlot - 150;   // ~1 minute ago

    const subscriptionRequest: SubscribeRequest = {
      // ... your subscription config
      fromSlot: testStartSlot, // u64 number
    };

    // Stop processing when reaching the test end slot
    const stream = await subscribe(config, subscriptionRequest,
      async (data) => {
        const slot = Number(data.transaction?.slot ?? data.account?.slot ?? 0);
        if (slot >= testEndSlot) {
          stream.cancel();
          return;
        }
        // your test handler here
      }
    );
    ```
  </Accordion>
</AccordionGroup>

## 下一步

<CardGroup cols={2}>
  <Card title="LaserStream gRPC" icon="bolt" href="/docs/zh/laserstream/grpc">
    了解有关gRPC流功能和特点的更多信息
  </Card>

  <Card title="开始使用" icon="rocket" href="https://dashboard.helius.dev/laserstream">
    从您的Helius控制台启用LaserStream并开始流式传输。
  </Card>

  <Card title="SDK文档" icon="github" href="https://github.com/helius-labs/laserstream-sdk">
    查看完整的SDK文档
  </Card>

  <Card title="联系支持" icon="headset" href="/docs/zh/support">
    获取实施帮助
  </Card>
</CardGroup>
