Dibi a zanořené transakce

jannek19
Člen | 47
+
0
-

Dobrý den,

jak řešíte zanořené transakce v Dibi? Typický problém, na který narážím je, že ve fasádě něco persistuji do více tabulek, otevřu si tedy transakci, volám jednotlivé repositáře pro uložení dat, na konci metody transakci commitnu (nebo zavolám rollback při selhání).

Potíž nastane, když si nějaký repositář také z nějakého dobrého důvodu otevře transakci, tu pak commitne, ale tím mi ukončí transkaci otevřenou ve fasádě.

Obvykle používám MySQL a mysqli driver v Dibi.

Jak podobné situace řešíte? Co byste v podobných situacích doporučili? Používáte nějakou knihovnu?

Díky moc.

Tharos
Člen | 1030
+
+3
-

Nevím teď od stolu, zda to nemá dibi nějak vyřešené, ale univerzálním řešením je vytvoření si služby (takové proxy) pro řízení transakcí. Řekněme nějaký Transactor… Skelet může být úplně jednoduchý:

class Transactor
{

	/**
	 * @var Connection
	 */
	private $connection;


	/**
	 * @param Connection $connection
	 */
	public function __construct(Connection $connection)
	{
		$this->connection = $connection;
	}


	public function begin()
	{
		$this->connection->beginTransaction();
	}


	public function commit()
	{
		$this->connection->commit();
	}


	public function rollback()
	{
		$this->connection->rollBack();
	}


	/**
	 * @param Closure $func
	 */
	public function transactional(Closure $func)
	{
		// ...
	}

}

No a do něj si pak jen doplníš podporu pro zanoření, stačí tam jen hlídat nějaký int private $level

Editoval Tharos (16. 8. 2017 12:19)

Tharos
Člen | 1030
+
0
-

Dodám, že tenhle přístup sám využívám, protože umožňuje řídit transakci i z fasád na vyšší úrovni, které jinak o nějakém Connection nemají ani tucha. :)

Používám přímo kód výše, ale ve spojení s Doctrine DBAL, které si samo řeší zanořování transakcí na úrovni Connection… Proto ten $level v mém kódu není.

jannek19
Člen | 47
+
0
-

Díky moc za odpověď. Obalit connection do nějaké pomocné třídy, která bude počítat zanoření, mě taky napadlo, spíš jsem ale uvažoval směrem, který je naznačený v tomto článku, kdy je každá transakce reprezentována samostatným objektem. Problém popsaný v článku by ale měla řešit metoda transactional v tvé ukázce.

Jen ještě přemýšlím nad tím, jestli obyčejné počítání levelu bude stačit a hlavně jak se to má zachovat při rollbacku. Begin a commit je jasný (level++, resp. level--), jak se ale zachovat při rollbacku – udělat jen level--, nebo provést reset level=0? Asi se budu muset podívat, jak to řeší např. Doctrine, případně nedávno tu někdo (@Felix?) dával knihovnu pro Nette Database.

Tharos
Člen | 1030
+
0
-

U rollbacku se dělá $level--. Koncepčně můžeš implementaci obšlehnout z Doctrine, je v ní hezky naimplementovaná i ta transactional metoda.

Felix
Nette Core | 1183
+
+2
-

@jannek19

Ahoj, jak popisuje @Tharos, ten jeho priklad je dost presny. Ja se pokusil implementovat nested transaction s vyuzitim neuzavrenych transakci podle clanku od @OndřejMirtes.

Mrkni: https://github.com/…cs/README.md#…

Je to uplne easy. Btw, podobnou techniku s $inst->transactional() pouzivam take (https://github.com/…/Transaction).

jannek19
Člen | 47
+
0
-

@Felix tvoje implementace je díky save pointům docela přímočará; implementace v Doctrine DBAL je kvůli tomu, že umožňuje i použití bez save pointů trochu složitější, ale oboje vypadá docela snadně.

Jen škoda, že z toho ještě nikdo neudělal rozšíření pro Dibi :)


Ještě jednou díky vám oběma.

Felix
Nette Core | 1183
+
0
-

jannek19 napsal(a):

@Felix tvoje implementace je díky save pointům docela přímočará; implementace v Doctrine DBAL je kvůli tomu, že umožňuje i použití bez save pointů trochu složitější, ale oboje vypadá docela snadně.

Jen škoda, že z toho ještě nikdo neudělal rozšíření pro Dibi :)


Ještě jednou díky vám oběma.

Pro dibi to muzeme jednoduse backportovat z NetteDatabase.