-
Bug
-
Resolution: Fixed
-
High
-
5.0.0, 5.7.0
-
1
-
Severity 3 - Minor
-
Prior to 5.0, PreReceiveRepositoryHook and PostReceiveRepositoryHook modules were run with no active transaction. If a transaction was in-flight when they were called, that transaction was suspended. That ensured any methods hooks called that accessed the database would create read-only and read-write transactions as required, based on what they were doing.
Starting with 5.0, the new PreRepositoryHook and PostRepositoryHook instances are now run as part of any transaction that happens to be in-flight when the RepositoryHookService is called. Since PreReceiveRepositoryHook and PostReceiveRepositoryHook instances are now implemented using PreRepositoryHook and PostRepositoryHook adapters, respectively, this means they may also be called as part of an in-flight transaction.
In some cases this results in hooks being called as part of a read-only transaction, which results in unexpected and often difficult to debug behavior. For example, creating a new comment as part of a hook may run successfully, but then after the hook completes the comment wasn't actually added. This happens because the comment is created inside a read-only transaction and its creation is rolled back instead of being committed.
- mentioned in
-
Page Failed to load
This change in behavior will affect MergeRequestCheck and RepositoryMergeCheck implementations as well.
Pending releases with the fixed behavior (5.6.3, 5.7.1 and 5.8.0), a workaround plugin developers can apply is to wrap their database accesses in SAL's TransactionTemplate. This is a best practice regardless of this bug. When add-ons interact with the database, they should use the TransactionTemplate to explicitly demarcate their transactions. This allows add-on developers more control over their data consistency.
The TransactionTemplate can be imported using whatever mechanism is being used to import the CommentService, or other database-using service. (Comments seem to be the most common case where plugin developers are encountering this issue.) For those importing the interface using atlassian-plugin.xml, the import would look like this:
Those using the Spring Scanner approach can just add @ComponentImport TransactionTemplate transactionTemplate to their constructor.
Using TransactionTemplate.execute, the add-on code inside the TransactionCallback will always be executed in a read/write transaction, ensuring changes to the database are committed (unless the callback throws an exception).