Skip to content

Commit d5d6498

Browse files
committed
error within transaction
1 parent 2f9feb4 commit d5d6498

File tree

9 files changed

+87
-2
lines changed

9 files changed

+87
-2
lines changed

changelog.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
## [8.2.0]
10+
### Added
11+
- Add exception `TransactionStartException`
12+
913
## [8.1.1] - 2022-03-18
1014
### Updated
1115
- Expanding the error description. Helps to reduce the number of issue.
@@ -815,7 +819,8 @@ The operation is now executed in the transaction and updates the new `refund` fi
815819
- Exceptions: AmountInvalid, BalanceIsEmpty.
816820
- Models: Transfer, Transaction.
817821

818-
[Unreleased]: https://github.com/bavix/laravel-wallet/compare/8.1.1...develop
822+
[Unreleased]: https://github.com/bavix/laravel-wallet/compare/8.2.0...develop
823+
[8.2.0]: https://github.com/bavix/laravel-wallet/compare/8.1.1...8.2.0
819824
[8.1.1]: https://github.com/bavix/laravel-wallet/compare/8.1.0...8.1.1
820825
[8.1.0]: https://github.com/bavix/laravel-wallet/compare/8.0.6...8.1.0
821826
[8.0.6]: https://github.com/bavix/laravel-wallet/compare/8.0.5...8.0.6

docs/_sidebar.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
- [Transfer](wallet-transfer)
2424
- [Transaction Filter](transaction-filter)
2525

26+
- Nova
27+
28+
- [Change of balance](nova-action)
29+
2630
- Purchases
2731

2832
- [Payment](payment)

docs/nova-action.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
## Nova Action
2+
3+
As you know, the package works with internal state. You can read more [here](https://github.com/bavix/laravel-wallet/pull/412) and [here](https://github.com/bavix/laravel-wallet/issues/455).
4+
5+
The action runs inside a transaction, which means you need to reset the transaction manually.
6+
7+
```php
8+
use Illuminate\Support\Facades\DB;
9+
10+
public function handle(ActionFields $fields, Collection $models)
11+
{
12+
DB::rollBack(0);
13+
...
14+
}
15+
```
16+
17+
Yes, it may not be convenient for someone, but you have to measure it. At the moment, there is no other solution.
18+
19+
But what if you want to use a transaction?
20+
Use according to [documentation](transaction).
21+
22+
Why was the decision made to move away from embedded transactions?
23+
The problem with embedded transactions is that the package changes the state of not only the database, but also the cache systems. Inside the transaction, sagas are implemented that update the balance in the cache systems of an already successful update inside the database.
24+
This feature was well described by me in the [pull request](https://github.com/bavix/laravel-wallet/pull/412).
25+
26+
It worked!

docs/transaction.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## Transaction
22

3+
> Is it possible to use laravel's inline transactions? No, It is Immpossible. This limitation is due to the internal architecture of the package. To achieve the maximum speed of work, work with an internal state of balance was needed. Starting with version 8.2, a special error has appeared that will inform you about incorrect work with the `TransactionStartException` package.
4+
35
Sometimes you need to execute many simple queries. You want to keep the data atomic. To do this, you need `laravel-wallet` v7.1+.
46

57
It is necessary to write off the amount from the balance and raise the ad in the search. What happens if the service for raising an ad fails? We wrote off the money, but did not raise the ad. Received reputational losses. We can imagine the opposite situation, we first raise the ad in the search, but it does not work to write off the money. There are not enough funds. This functionality will help to solve all this. We monitor ONLY the state of the wallet, the rest falls on the developer. Let's take an unsuccessful lift, for example.

src/Internal/Exceptions/ExceptionInterface.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ interface ExceptionInterface extends Throwable
2121
public const TRANSACTION_FAILED = 1 << 10;
2222
public const MODEL_NOT_FOUND = 1 << 11;
2323
public const UNKNOWN_EVENT = 1 << 12;
24+
public const TRANSACTION_START = 1 << 13;
2425
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Bavix\Wallet\Internal\Exceptions;
6+
7+
use LogicException;
8+
9+
final class TransactionStartException extends LogicException implements LogicExceptionInterface
10+
{
11+
}

src/Internal/Service/DatabaseService.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Bavix\Wallet\Internal\Exceptions\ExceptionInterface;
88
use Bavix\Wallet\Internal\Exceptions\TransactionFailedException;
9+
use Bavix\Wallet\Internal\Exceptions\TransactionStartException;
910
use Bavix\Wallet\Services\RegulatorServiceInterface;
1011
use Illuminate\Config\Repository as ConfigRepository;
1112
use Illuminate\Database\ConnectionInterface;
@@ -17,6 +18,8 @@ final class DatabaseService implements DatabaseServiceInterface
1718
{
1819
private ConnectionInterface $connection;
1920

21+
private bool $init = false;
22+
2023
public function __construct(
2124
ConnectionResolverInterface $connectionResolver,
2225
private RegulatorServiceInterface $regulatorService,
@@ -34,15 +37,27 @@ public function __construct(
3437
*/
3538
public function transaction(callable $callback)
3639
{
40+
$level = $this->connection->transactionLevel();
41+
if ($level > 0 && !$this->init) {
42+
throw new TransactionStartException(
43+
'Working inside an embedded transaction is not possible. https://bavix.github.io/laravel-wallet/#/transaction',
44+
ExceptionInterface::TRANSACTION_START,
45+
);
46+
}
47+
48+
$this->init = true;
49+
3750
try {
38-
if ($this->connection->transactionLevel() > 0) {
51+
if ($level > 0) {
3952
return $callback();
4053
}
4154

4255
$this->regulatorService->purge();
4356

4457
return $this->connection->transaction(function () use ($callback) {
4558
$result = $callback();
59+
$this->init = false;
60+
4661
if ($result === false || (is_countable($result) && count($result) === 0)) {
4762
$this->regulatorService->purge();
4863
} else {
@@ -53,10 +68,12 @@ public function transaction(callable $callback)
5368
});
5469
} catch (RecordsNotFoundException|ExceptionInterface $exception) {
5570
$this->regulatorService->purge();
71+
$this->init = false;
5672

5773
throw $exception;
5874
} catch (Throwable $throwable) {
5975
$this->regulatorService->purge();
76+
$this->init = false;
6077

6178
throw new TransactionFailedException(
6279
'Transaction failed. Message: '.$throwable->getMessage(),

src/Internal/Service/DatabaseServiceInterface.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66

77
use Bavix\Wallet\Internal\Exceptions\ExceptionInterface;
88
use Bavix\Wallet\Internal\Exceptions\TransactionFailedException;
9+
use Bavix\Wallet\Internal\Exceptions\TransactionStartException;
910
use Illuminate\Database\RecordsNotFoundException;
1011

1112
interface DatabaseServiceInterface
1213
{
1314
/**
1415
* @throws RecordsNotFoundException
16+
* @throws TransactionStartException
1517
* @throws TransactionFailedException
1618
* @throws ExceptionInterface
1719
*

tests/Units/Service/DatabaseTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@
66

77
use Bavix\Wallet\Internal\Exceptions\ExceptionInterface;
88
use Bavix\Wallet\Internal\Exceptions\TransactionFailedException;
9+
use Bavix\Wallet\Internal\Exceptions\TransactionStartException;
910
use Bavix\Wallet\Internal\Service\DatabaseServiceInterface;
1011
use Bavix\Wallet\Test\Infra\TestCase;
12+
use Illuminate\Support\Facades\DB;
1113

1214
/**
1315
* @internal
1416
*/
1517
final class DatabaseTest extends TestCase
1618
{
19+
/**
20+
* @throws ExceptionInterface
21+
*/
1722
public function testCheckCode(): void
1823
{
1924
$this->expectException(TransactionFailedException::class);
@@ -23,4 +28,16 @@ public function testCheckCode(): void
2328
throw new \RuntimeException();
2429
});
2530
}
31+
32+
/**
33+
* @throws ExceptionInterface
34+
*/
35+
public function testCheckInTransaction(): void
36+
{
37+
$this->expectException(TransactionStartException::class);
38+
$this->expectExceptionCode(ExceptionInterface::TRANSACTION_START);
39+
40+
DB::beginTransaction();
41+
app(DatabaseServiceInterface::class)->transaction(static fn (): int => 42);
42+
}
2643
}

0 commit comments

Comments
 (0)