承接 apntalk/esl-replay 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

apntalk/esl-replay

Composer 安装命令:

composer require apntalk/esl-replay

包简介

Durable replay artifact platform for FreeSWITCH ESL runtime output

README 文档

README

Durable replay artifact platform for FreeSWITCH ESL runtime output.

What this package is

apntalk/esl-replay provides durable storage, deterministic reads, restart-safe progress recovery, offline replay, and bounded recovery/evidence reconstruction for FreeSWITCH ESL artifacts emitted by apntalk/esl-react.

What this package is NOT

This package does not:

  • manage live FreeSWITCH socket connections
  • perform reconnect supervision
  • own live ESL session state
  • restore live runtime continuity after a process restart
  • execute business telephony logic
  • embed Laravel-specific persistence abstractions

See docs/architecture.md for the full boundary description.

Package family

Package Role
apntalk/esl-core Protocol substrate, frame/event primitives, shared vocabulary
apntalk/esl-react Live async runtime, session supervision, artifact emission
apntalk/esl-replay Durable storage, deterministic reads, checkpoints, offline replay, recovery/evidence reconstruction
apntalk/laravel-freeswitch-esl Laravel integration and operational control plane

Requirements

  • PHP 8.2+

Installation

composer require apntalk/esl-replay

Quick start: storing artifacts

use Apntalk\EslReplay\Config\ReplayConfig;
use Apntalk\EslReplay\Config\StorageConfig;
use Apntalk\EslReplay\Storage\ReplayArtifactStore;

$store = ReplayArtifactStore::make(new ReplayConfig(
    storage: new StorageConfig('/var/replay/artifacts'),
));

// $artifact implements CapturedArtifactEnvelope (emitted by esl-react)
$id = $store->write($artifact);

StorageConfig currently supports:

  • filesystem with a storage directory path
  • sqlite with a SQLite database file path
  • database as a compatibility alias for the current SQLite-backed adapter

The same storagePath constructor argument is interpreted by adapter: a filesystem store expects a directory, while the SQLite-backed adapters expect a database file path.

For this release, SQLite preserves the same read/order contract as the filesystem adapter for a single active writer epoch. If a second long-lived SQLite store instance writes against the same database without reopening after another writer has advanced the stream, the write fails explicitly.

PostgreSQL is not implemented in this release.

Quick start: reading artifacts

use Apntalk\EslReplay\Read\ReplayReadCriteria;

// Read from the beginning
$cursor  = $store->openCursor();
$records = $store->readFromCursor($cursor, limit: 100);

foreach ($records as $record) {
    echo "{$record->appendSequence}: {$record->artifactName}" . PHP_EOL;
    // Advance cursor to track progress
    $cursor = $cursor->advance($record->appendSequence);
}

// Look up a specific record by id
$record = $store->readById($id);

// Apply conservative bounded filters while preserving append order
$criteria = new ReplayReadCriteria(
    artifactName: 'event.raw',
    replaySessionId: 'replay-session-001',
    pbxNodeSlug: 'pbx-a',
);

$filtered = $store->readFromCursor($store->openCursor(), limit: 100, criteria: $criteria);

Bounded reader filtering currently supports inclusive capture-time windows plus exact matching on artifactName, jobUuid, replaySessionId, pbxNodeSlug, workerSessionId, sessionId, and connectionGeneration. This is not a general query engine; filtered reads still return records in append-sequence order within the adapter stream.

On the filesystem path, ordinary reads intentionally skip malformed persisted lines so valid stored records remain readable after a partial or interrupted tail write. Retention/rewrite flows are stricter and fail explicitly if malformed retained input is discovered.

Quick start: checkpoint and resume

use Apntalk\EslReplay\Checkpoint\ExecutionResumeState;
use Apntalk\EslReplay\Checkpoint\FilesystemCheckpointStore;
use Apntalk\EslReplay\Checkpoint\ReplayCheckpointCriteria;
use Apntalk\EslReplay\Checkpoint\ReplayCheckpointReference;
use Apntalk\EslReplay\Checkpoint\ReplayCheckpointRepository;
use Apntalk\EslReplay\Checkpoint\ReplayCheckpointService;
use Apntalk\EslReplay\Config\CheckpointConfig;

$checkpointStore = FilesystemCheckpointStore::make(
    new CheckpointConfig('/var/replay/checkpoints', 'my-processor')
);
$service = new ReplayCheckpointService($checkpointStore, 'my-processor');
$repository = new ReplayCheckpointRepository($checkpointStore);

// At startup: resolve where to start reading
$state  = ExecutionResumeState::resolve($checkpointStore, 'my-processor');
$cursor = $state->cursor; // ReplayReadCursor::start() or saved position

// Process artifacts...
foreach ($store->readFromCursor($cursor, 100) as $record) {
    // ... process record ...
    $cursor = $cursor->advance($record->appendSequence);
    $service->save($cursor); // save progress after each record (or batch)
}

// Save a checkpoint with explicit operational identity anchors
$repository->save(
    new ReplayCheckpointReference(
        key: 'worker-a',
        replaySessionId: 'replay-session-001',
        jobUuid: 'job-123',
        pbxNodeSlug: 'pbx-a',
        workerSessionId: 'worker-a',
    ),
    $cursor,
);

// Later: bounded checkpoint lookup for drain/resume workflows
$matches = $repository->find(new ReplayCheckpointCriteria(
    replaySessionId: 'replay-session-001',
    workerSessionId: 'worker-a',
));

Important: A checkpoint restores artifact-processing progress only. It does NOT restore a live FreeSWITCH socket, ESL session, or any runtime continuity.

FilesystemCheckpointStore, ReplayCheckpointService, ExecutionResumeState, and ReplayCheckpointRepository are the supported checkpoint path in this release.

Quick start: offline replay

use Apntalk\EslReplay\Config\ExecutionConfig;
use Apntalk\EslReplay\Execution\OfflineReplayExecutor;
use Apntalk\EslReplay\Execution\ReplayExecutionCandidate;
use Apntalk\EslReplay\Execution\ReplayHandlerRegistry;
use Apntalk\EslReplay\Execution\InjectionResult;
use Apntalk\EslReplay\Contracts\ReplayInjectorInterface;

$executor = OfflineReplayExecutor::make(
    new ExecutionConfig(dryRun: true),
    $store,
);

// Plan: inspect what would be replayed
$plan = $executor->plan($store->openCursor());
echo "Records to replay: {$plan->recordCount}" . PHP_EOL;

// Execute: dry-run produces no side effects
$result = $executor->execute($plan);
echo "Skipped (dry-run): {$result->skippedCount}" . PHP_EOL;

// Optional bounded handler dispatch in non-dry-run mode
$executorWithHandlers = OfflineReplayExecutor::make(
    new ExecutionConfig(dryRun: false),
    $store,
    new ReplayHandlerRegistry([
        'api.dispatch' => $myApiDispatchHandler,
    ]),
);

// Optional guarded re-injection remains explicit and caller-supplied.
$injector = new class implements ReplayInjectorInterface {
    public function inject(ReplayExecutionCandidate $candidate): InjectionResult
    {
        // Dispatch through caller-owned transport here.
        return new InjectionResult('injected');
    }
};

$reinjectionExecutor = OfflineReplayExecutor::make(
    new ExecutionConfig(
        dryRun: true,
        reinjectionEnabled: true,
        reinjectionArtifactAllowlist: ['api.dispatch'],
    ),
    $store,
    null,
    $injector,
);

Offline replay operates only on stored artifacts. It does NOT require a live FreeSWITCH socket. Handler dispatch is explicit and bounded by exact artifact name matching; unhandled records remain observational.

Quick start: recovery evidence reconstruction

use Apntalk\EslReplay\Checkpoint\ReplayCheckpointReference;
use Apntalk\EslReplay\Recovery\CheckpointReconstructionWindowResolver;
use Apntalk\EslReplay\Recovery\RecoveryEvidenceEngine;

$repository = new ReplayCheckpointRepository($checkpointStore);
$checkpoint = $repository->save(
    new ReplayCheckpointReference(
        key: 'worker-a',
        replaySessionId: 'replay-session-001',
        recoveryGenerationId: 'generation-7',
    ),
    $cursor,
);

$resolver = new CheckpointReconstructionWindowResolver($store);
$window = $resolver->resolve($checkpoint);

$engine = RecoveryEvidenceEngine::make($store);
$bundle = $engine->reconstruct($window);

echo $bundle->manifest->bundleId . PHP_EOL;
echo $bundle->manifest->verdict->posture . PHP_EOL;

Recovery/evidence reconstruction:

  • consumes stored artifacts only
  • preserves append-order semantics
  • reconstructs bounded runtime truth from stored payload/runtime metadata
  • emits deterministic machine-readable bundles and scenario comparisons
  • fails closed when stored artifacts are insufficient to support a bounded claim

It does not restore a live ESL socket, live runtime supervision state, or session continuity after restart.

Safety note on re-injection

Controlled protocol re-injection is optional, disabled by default, and higher risk than ordinary offline replay. It now requires all of the following:

  • ExecutionConfig::reinjectionEnabled = true
  • an explicit reinjectionArtifactAllowlist
  • a caller-supplied ReplayInjectorInterface

Only allowlisted executable artifact types currently become reinjection candidates:

  • api.dispatch
  • bgapi.dispatch

Observational artifacts remain non-injectable by default. Dry-run remains safe: it reports what would be reinjected without invoking the injector.

Quick start: filesystem retention

use Apntalk\EslReplay\Checkpoint\FilesystemCheckpointStore;
use Apntalk\EslReplay\Checkpoint\ReplayCheckpointCriteria;
use Apntalk\EslReplay\Config\CheckpointConfig;
use Apntalk\EslReplay\Retention\CheckpointAwarePruner;
use Apntalk\EslReplay\Retention\PrunePolicy;

$checkpointStore = FilesystemCheckpointStore::make(
    new CheckpointConfig('/var/replay/checkpoints', 'my-processor')
);

$pruner = new CheckpointAwarePruner('/var/replay/artifacts');
$policy = new PrunePolicy(
    maxRecordAge: new DateInterval('P7D'),
    maxStreamBytes: 10_000_000,
    protectedRecordCount: 500,
);

// Plan first: inspect what would be pruned.
$plan = $pruner->planForCheckpointQuery(
    $checkpointStore,
    new ReplayCheckpointCriteria(
        replaySessionId: 'replay-session-001',
        workerSessionId: 'my-processor',
        limit: 100,
    ),
    $policy,
);

// Apply explicitly after reviewing the plan.
$result = $pruner->pruneForCheckpointQuery(
    $checkpointStore,
    new ReplayCheckpointCriteria(
        replaySessionId: 'replay-session-001',
        workerSessionId: 'my-processor',
        limit: 100,
    ),
    $policy,
);

Retention is filesystem-backed in this release. It prunes only an ordered prefix, validates active checkpoint compatibility before pruning, preserves protected record windows, and fails explicitly on malformed retained input. Checkpoint query pruning fails closed when the query resolves no active checkpoints; pass allowEmptyCheckpointQuery: true only when an uncheckpointed prune is intentional.

Opt-in live verification

For RC validation, pre-release verification, or manual support checks against a real FreeSWITCH PBX, use the opt-in live harness documented in docs/live-testing.md.

It is intentionally separate from the default PHPUnit and CI flow. It reads live ESL credentials from .env.live.local, validates the replay package surface using live-derived artifacts, and must not expose secrets from that file.

Documentation

License

MIT

apntalk/esl-replay 适用场景与选型建议

apntalk/esl-replay 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 14 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 04 月 16 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「freeswitch」 「replay」 「event-sourcing」 「esl」 「artifact-storage」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 apntalk/esl-replay 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 apntalk/esl-replay 我们能提供哪些服务?
定制开发 / 二次开发

基于 apntalk/esl-replay 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 14
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 47
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-04-16