Completed
Push — master ( 2078b9...d52adb )
by SuRaMoN
02:36
created

TimedAutoTransactionBatchIterator::valid()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.2
c 0
b 0
f 0
cc 4
eloc 13
nc 5
nop 0
1
<?php
2
3
namespace itertools;
4
5
use Exception;
6
use IteratorIterator;
7
use itertools\IterUtil;
8
use PDO;
9
10
/**
11
 * When adding this itertator to a for loop you can automatically
12
 * batch updates in a transaction to speed up inserts or updates.
13
 *
14
 * Example:
15
 * foreach (new TimedAutoTransactionBatchIterator($entities, $pdo) as $entity) {
16
 *   $this->pdo->exec('UPDATE entity SET counter = counter + 1 WHERE id = ' . (int) $entity->getId());
17
 *   // Batching updates in a transaction can be have a huge performance improvement
18
 *   // By default the commit happens every second. Be aware, long transactions can cause dead locks
19
 * }
20
 */
21
class TimedAutoTransactionBatchIterator extends IteratorIterator
22
{
23
    const START_TRANSACTION = -1;
24
25
    protected $timeout;
26
    protected $pdo;
27
    protected $nextCommitTime = self::START_TRANSACTION;
28
    protected $inTransaction = false;
29
30
    public function __construct($iterator, PDO $pdo, $timeout = 1.0 /** s */)
31
    {
32
        parent::__construct(IterUtil::asTraversable($iterator));
33
        $this->timeout = $timeout;
34
        $this->pdo = $pdo;
35
    }
36
37
    public function valid()
38
    {
39
        $valid = parent::valid();
40
        if (! $valid) {
41
            $this->commitIfInTransaction();
42
            return false;
43
        }
44
        if (microtime(true) >= $this->nextCommitTime) {
45
            $this->nextCommitTime = self::START_TRANSACTION;
46
            $this->commitIfInTransaction();
47
        }
48
        if (self::START_TRANSACTION == $this->nextCommitTime) {
49
            $this->pdo->beginTransaction();
50
            $this->inTransaction = true;
51
            $this->nextCommitTime = microtime(true) + $this->timeout;
52
        }
53
        return true;
54
    }
55
56
    public function commitIfInTransaction()
57
    {
58
        if(! $this->inTransaction) {
59
            return;
60
        }
61
        try {
62
            $this->pdo->commit();
63
            $this->inTransaction = false;
64
        } catch(Exception $e) {
65
            $this->pdo->rollBack();
66
            $this->inTransaction = false;
67
            throw $e;
68
        }
69
    }
70
71
    public function __destruct()
72
    {
73
        if (! $this->inTransaction) {
74
            return;
75
        }
76
        // this can only be destructed in transaction if exception occured
77
        $this->pdo->rollBack();
78
    }
79
}
80
81