承接 xint0/laravel-optimistic-locking 相关项目开发

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

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

xint0/laravel-optimistic-locking

Composer 安装命令:

composer require xint0/laravel-optimistic-locking

包简介

Adds optimistic locking feature to eloquent models.

README 文档

README

GitLab (self-managed) Packagist PHP Version Packagist PHP Version Gitlab pipeline status (self-managed) Gitlab code coverage (self-managed, specific job) GitLab last commit Packagist Downloads (custom server) Packagist Version (custom server)

Adds optimistic locking to Eloquent models.

This package replaces reshadman/laravel-optimistic-locking.

Installation

composer require xint0/laravel-optimistic-locking

Compatibility

Starting with version 2.0.0 will only support Laravel versions under support:

Laravel VersionLaravel Optimistic Locking Version
5.5 - 111.3.6
11 - 122.x
12 - 133.x

Usage

Basic usage

Use the \Reshadman\OptimisticLocking\OptimisticLocking trait in your model:

<?php

use Reshadman\OptimisticLocking\OptimisticLocking;

class BlogPost extends Model {
    use OptimisticLocking;
}

Add the nullable integer lock_version field to the table of the model:

<?php # migration to add lock version column

$schema->integer('lock_version')->unsigned()->nullable();

Then you are ready to go, if the same resource is edited by two different processes CONCURRENTLY then the following exception will be raised:

<?php

use Reshadman\OptimisticLocking\StaleModelLockingException;

You should catch the above exception and act properly based on your business logic.

Maintaining lock_version during business transactions

You can keep track of a lock version during a business transaction by informing your API or HTML client about the current version:

<input type="hidden" name="lock_version" value="{{$blogPost->lock_version}}" 

and in controller:

<?php

use Reshadman\OptimisticLocking\StaleModelLockingException;
// Explicitly setting the lock version
class PostController {
    public function update($id)
    {
        $post = Post::findOrFail($id);
        $post->lock_version = request('lock_version');
        try {
            $post->save();
            // You can also define more implicit reusable methods in your model like Model::saveWithVersion(...$args); 
            // or just override the default Model::save(...$args); method which accepts $options
            // Then automatically read the lock version from Request and set into the model.
        } catch (StaleModelLockingException $exception) {
            // Occurs when request lock version does not match value stored in database. Which means another user
            // has already updated the model.
        }
    }
}

So if two authors are editing the same content concurrently, you can keep track of your Read State, and ask the second author to rewrite his changes.

Disabling and enabling optimistic locking

You can disable and enable optimistic locking for a specific instance:

<?php
$blogPost->disableLocking();
//
//save model does not verify lock version
//
$blogPost->enableLocking();
//
// save model verifies lock version
//

By default, optimistic locking is enabled when you use OptimisticLocking trait in your model, to alter the default behavior you can set the lock strictly to false:

<?php

use Illuminate\Database\Eloquent\Model;
use Reshadman\OptimisticLocking\OptimisticLocking;

class BlogPost extends Model 
{
    use OptimisticLocking;
    
    protected $lock = false;
}

and then you may enable it: $blogPost->enableLocking();

Soft-deletes with optimistic locking

New feature in version 2.1.0 and 3.1.0

The Reshadman\OptimisticLocking\OptimisticLockingSoftDeletes trait combines the soft deleting functionality from Laravel with optimistic locking:

use Illuminate\Database\Eloquent\Model;
use Reshadman\OptimisticLocking\OptimisticLockingSoftDeletes;

class BlogPost extends Model
{
    use OptimisticLockingSoftDeletes;
}

Now the BlogPost class combines soft-deleting with optimistic-locking.

Use a different column for tracking version

By default, the lock_version column is used for tracking version, you can alter that by overriding the following method of the trait:

<?php

use Illuminate\Database\Eloquent\Model;
use Reshadman\OptimisticLocking\OptimisticLocking;

class BlogPost extends Model
{
    use OptimisticLocking;
    
    /**
     * Name of the lock version column.
     *
     * @return string
     */
    protected static function lockVersionColumn()
    {
        return 'track_version';
    }
}

What is optimistic locking?

For detailed explanation read the concurrency section of Patterns of Enterprise Application Architecture by Martin Fowler.

There are two ways to approach generic concurrency race conditions:

  1. Do not allow other processes (or users) to read and update the same resource (Pessimistic Locking).
  2. Allow other processes to read the same resource concurrently, but do not allow further update, if one of the processes updated the resource before the others (Optimistic locking).

Laravel allows Pessimistic locking as described in the documentation, this package allows you to have Optimistic locking in a rails like way.

What happens during an optimistic lock?

Every time you perform an update action to your resource(model), the lock_version counter field in the table is incremented by 1. If you read a resource and another process updates the resource after you read it, the true version counter is incremented by one. If the current process attempts to update the model, simply a StaleModelLockingException will be thrown, and you should handle the race condition (merge, retry, ignore) based on your business logic. That is simply via adding the following criteria to the update query of an optimistically lockable model:

<?php
$query->where('id', $this->id)
    ->where('lock_version', $this->lock_version)
    ->update($changes);

If the resource has been updated before your update attempt, then the above will simply update no records, and it means that the model has been updated before current attempt, or that it has been deleted.

Why don't we use updated_at for tracking changes?

Because they may remain the same during two concurrent updates.

Contributing

This package is open source software, and you are encouraged to contribute by:

Working with the code

The project uses Docker Compose to define containers used for development. Although it is not necessary to use Docker, it makes working with multiple versions of PHP easier.

After forking and cloning the git repository to your local development environment you can execute the following docker compose commands:

Install dependencies

docker compose run --rm php composer install

Execute tests

docker compose run --rm php ./vendor/bin/phpunit

Execute tests with coverage reporting

docker compose run --rm --env=XDEBUG_MODE=coverage php php ./vendor/bin/phpunit --coverage-text

Execute PHPStan analysis

docker compose run --rm phpstan

License

The MIT License (MIT). Please see License File for more information.

xint0/laravel-optimistic-locking 适用场景与选型建议

xint0/laravel-optimistic-locking 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 105.81k 次下载、GitHub Stars 达 2, 最近一次更新时间为 2020 年 09 月 21 日, 在 PHP 生态内属于活跃度较高的组件。

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

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

围绕 xint0/laravel-optimistic-locking 我们能提供哪些服务?
定制开发 / 二次开发

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

BUG 修复 & 性能优化

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

项目外包 & 长期维护

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

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

统计信息

  • 总下载量: 105.81k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 3
  • 点击次数: 25
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

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

其他信息

  • 授权协议: MIT
  • 更新时间: 2020-09-21