承接 aeatech/transaction-manager-core 相关项目开发

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

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

aeatech/transaction-manager-core

Composer 安装命令:

composer require aeatech/transaction-manager-core

包简介

Transaction manager core

README 文档

README

Code Coverage

A lightweight, DB-agnostic transaction manager designed for high-load environments, with first-class support for:

  • retries
  • exponential backoff
  • transient/connection error classification
  • commit-unknown detection
  • strict idempotency contracts
  • pure SQL transaction execution
  • deferred build for dependent transactions

The core package contains no DB-specific logic.
MySQL and PostgreSQL integrations live in separate packages:

  • transaction-manager-doctrine-adapter (bridge for Doctrine DBAL)
  • transaction-manager-mysql
  • transaction-manager-postgresql

Features

  • Execute a sequence of SQL operations as a single logical transaction
  • Automatically retry on transient errors (deadlocks, serialization failures)
  • Automatically recovers from connection drops
  • Handle unknown commit outcome
  • IsolationLevel level control
  • Pure, deterministic execution plan
  • Deferred Build: support for dependent transactions (e.g., use generated IDs from previous steps)
  • Backoff strategies (exponential with jitter)
  • Pluggable error classifiers per-database
  • Optional prepared statement reuse via StatementReusePolicy (disabled by default)
  • No global state, no magic

If you need the full detailed contract describing all guarantees, responsibilities, idempotency rules, error semantics, and connection requirements, see:

👉 CONTRACT.md

Installation

composer require aeatech/transaction-manager-core

Quick Example

use AEATech\TransactionManager\TransactionManager;
use AEATech\TransactionManager\TxOptions;
use AEATech\TransactionManager\RetryPolicy;
use AEATech\TransactionManager\ExponentialBackoff;
use AEATech\TransactionManager\SystemSleeper;
use AEATech\TransactionManager\ExecutionPlanBuilder;
use AEATech\TransactionManager\TransactionInterface;
use AEATech\TransactionManager\Query;
use AEATech\TransactionManager\GenericErrorClassifier;

// Assume $connectionAdapter implements AEATech\TransactionManager\ConnectionInterface
$connectionAdapter = '...';

// Assume $errorClassifier implements AEATech\TransactionManager\ErrorClassifierInterface
$errorClassifier = new GenericErrorClassifier($errorHeuristics);

$tm = new TransactionManager(
    new ExecutionPlanBuilder(),
    $connectionAdapter,
    $errorClassifier,
    RetryPolicy::noRetry(), // Default retry policy
    new SystemSleeper()
);

class InsertUser implements TransactionInterface
{
    public function __construct(
        private string $email,
        private string $name
    ) {}

    public function build(): Query
    {
        return new Query(
            sql: "INSERT INTO users (email, name) VALUES (?, ?)",
            params: [$this->email, $this->name],
            types: [\PDO::PARAM_STR, \PDO::PARAM_STR]
        );
    }

    public function isIdempotent(): bool
    {
        return false; // inserting the same user twice is not safe
    }
}

$result = $tm->run(new InsertUser('a@example.com', 'Alice'));

echo $result->affectedRows; // → 1

Idempotent Transaction Example

class SetStock implements TransactionInterface
{
    public function __construct(
        private int $productId,
        private int $qty
    ) {}

    public function build(): Query
    {
        return new Query(
            sql: "UPDATE products SET stock = ? WHERE id = ?",
            params: [$this->qty, $this->productId],
            types: [\PDO::PARAM_INT, \PDO::PARAM_INT]
        );
    }

    public function isIdempotent(): bool
    {
        return true; // Setting a value twice results in the same state
    }
}

Executing Multiple Transactions as One

$tm->run([
    new SetStock(10, 5),
    new SetStock(11, 0),
    new InsertUser("a@example.com", "Alice"),
]);

Deferred Build (Dependent Transactions)

By default, TransactionInterface::build() is called before the database transaction starts. This ensures that the execution plan is deterministic and minimizes the database transaction's duration.

However, sometimes a transaction depends on data generated by a previous step in the same batch (e.g., using an AUTO_INCREMENT ID from a previous INSERT). In such cases, you can use the #[DeferredBuild] attribute.

How it works:

  1. When a transaction class is marked with #[DeferredBuild], its build() method is not called during the initial plan construction.
  2. Instead, TransactionManager calls build() inside the active database transaction, right before executing the query.
  3. This allows you to perform SELECT queries (e.g., via a repository) to fetch IDs generated by previous steps and use them to build the current query.

Example:

use AEATech\TransactionManager\Attribute\DeferredBuild;
use AEATech\TransactionManager\TransactionInterface;
use AEATech\TransactionManager\Query;

#[DeferredBuild]
class CreateProfile implements TransactionInterface
{
    public function __construct(
        private UserRepository $repository,
        private string $email,
        private array $profileData
    ) {}

    public function build(): Query
    {
        // This code runs INSIDE the DB transaction.
        // We can fetch the user ID that was inserted in the previous step.
        $user = $this->repository->findByEmail($this->email);
        
        return new Query(
            sql: "INSERT INTO profiles (user_id, bio) VALUES (?, ?)",
            params: [$user->id, $this->profileData['bio']]
        );
    }

    public function isIdempotent(): bool { return false; }
}

// Usage in a batch:
$tm->run([
    new InsertUser("a@example.com", "Alice"),
    new CreateProfile($userRepository, "a@example.com", ['bio' => 'Software Engineer'])
]);

Retry Options

Transaction Manager uses a default retry policy configured during instantiation. You can override this policy for specific transactions using TxOptions.

1. Default Retry Policy

When you create the TransactionManager, you must provide a default policy.

Note on maxRetries: The value specifies the number of additional attempts after the initial failure. For example, maxRetries: 3 means the transaction can be executed up to 4 times in total (1 initial attempt + 3 retries).

$defaultPolicy = new RetryPolicy(
    maxRetries: 3, // Total 4 attempts
    backoffStrategy: new ExponentialBackoff(baseDelayMs: 100)
);

$tm = new TransactionManager(
    $builder,
    $connection,
    $classifier,
    $defaultPolicy,
    $sleeper
);

2. Disabling Retries

If you want to ensure a transaction is executed exactly once without any retries, use RetryPolicy::noRetry():

// Globally (in constructor)
$tm = new TransactionManager(..., RetryPolicy::noRetry(), ...);

// Or for a specific call
$tm->run($tx, new TxOptions(retryPolicy: RetryPolicy::noRetry()));

3. Overriding Policy per Call

use AEATech\TransactionManager\RetryPolicy;
use AEATech\TransactionManager\TxOptions;
use AEATech\TransactionManager\IsolationLevel;
use AEATech\TransactionManager\ExponentialBackoff;

$customPolicy = new RetryPolicy(
    maxRetries: 5, // Total 6 attempts
    backoffStrategy: new ExponentialBackoff(baseDelayMs: 50)
);

$options = new TxOptions(
    isolationLevel: IsolationLevel::RepeatableRead,
    retryPolicy: $customPolicy // Overrides the default policy
);

$tm->run($transaction, $options);

Isolation level (optional)

TxOptions::$isolationLevel is optional.

  • If isolationLevel is null, Transaction Manager does not issue SET TRANSACTION ISOLATION LEVEL ... and the effective isolation level is whatever is currently configured for the connection/database (server default, pool/session settings, etc.).
  • If isolationLevel is set (e.g. ReadCommitted, RepeatableRead, Serializable), Transaction Manager will explicitly set it for the current transaction.

Retry with explicit isolation level

$options = new TxOptions(
    isolationLevel: IsolationLevel::RepeatableRead,
    retryPolicy: $customPolicy
);

$tm->run($transaction, $options);

Retry without changing isolation level (use DB/connection default)

$options = new TxOptions(
    isolationLevel: null,
    retryPolicy: $customPolicy
);

$tm->run($transaction, $options);

Prepared Statement Reuse Hint

StatementReusePolicy is an optional, best‑effort hint that may help a connection adapter optimize execution of similar queries. It does not affect correctness and may be ignored by an implementation.

Options:

  • StatementReusePolicy::None — no reuse (default).
  • StatementReusePolicy::PerTransaction — attempt to reuse a prepared statement within a single transaction.
  • StatementReusePolicy::PerConnection — attempt to reuse a prepared statement across transactions while the physical connection remains open.

Example usage in TransactionInterface::build():

use AEATech\TransactionManager\Query;
use AEATech\TransactionManager\StatementReusePolicy;

class InsertUser implements TransactionInterface
{
    public function __construct(
        private string $email,
        private string $name
    ) {}

    public function build(): Query
    {
        return new Query(
            sql: "INSERT INTO users (email, name) VALUES (?, ?)",
            params: [$this->email, $this->name],
            types: [\PDO::PARAM_STR, \PDO::PARAM_STR],
            statementReusePolicy: StatementReusePolicy::PerTransaction
        );
    }

    public function isIdempotent(): bool { return false; }
}

Testing

The project is configured to run tests in Isolated Docker containers for different PHP versions (8.2, 8.3, 8.4).

1. Start the Environment

Make sure the Docker containers are up and running. From the project root:

docker-compose -p aeatech-transaction-manager-core -f docker/docker-compose.yml up -d --build

2. Install Dependencies

Install composer dependencies inside the container (using PHP 8.2 as a base):

docker-compose -p aeatech-transaction-manager-core -f docker/docker-compose.yml exec php-cli-8.2 composer install

3. Run Tests

PHP 8.2

docker-compose -p aeatech-transaction-manager-core -f docker/docker-compose.yml exec php-cli-8.2 vendor/bin/phpunit

PHP 8.3

docker-compose -p aeatech-transaction-manager-core -f docker/docker-compose.yml exec php-cli-8.3 vendor/bin/phpunit

PHP 8.4

docker-compose -p aeatech-transaction-manager-core -f docker/docker-compose.yml exec php-cli-8.4 vendor/bin/phpunit

Run All Tests (Bash Script)

for v in 8.2 8.3 8.4; do \
    echo "Testing PHP $v..."; \
    docker-compose -p aeatech-transaction-manager-core -f docker/docker-compose.yml exec php-cli-$v vendor/bin/phpunit || break; \
done

4. Run phpstan

docker-compose -p aeatech-transaction-manager-core -f docker/docker-compose.yml exec php-cli-8.4 vendor/bin/phpstan analyse -c phpstan.neon --memory-limit=1G

Stopping the Environment

docker-compose -p aeatech-transaction-manager-core -f docker/docker-compose.yml down -v

License

This project is licensed under the MIT License. See the LICENSE file for details.

aeatech/transaction-manager-core 适用场景与选型建议

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

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

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

围绕 aeatech/transaction-manager-core 我们能提供哪些服务?
定制开发 / 二次开发

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

BUG 修复 & 性能优化

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

项目外包 & 长期维护

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

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

统计信息

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

GitHub 信息

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

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-01-11