Feature #11884
Implement lazy transaction initialization for full transaction blocks
100%
History
#2 Updated by Artur Școlnic about 2 months ago
- Status changed from New to WIP
- Assignee set to Artur Școlnic
#3 Updated by Artur Școlnic about 2 months ago
Deferring transaction activation until DML is actually needed¶
1. Problem¶
A converted full-transaction block opens a real database transaction as soon as any record buffer for that database is touched — read or write. A great deal of converted code has the shape "open a full transaction, test a condition, write only if the condition holds", so the common path opens and commits a transaction that never wrote anything.
DO TRANSACTION: /* TxWrapper created, inactive */
FIND FIRST customer NO-LOCK. /* openScope -> activate() -> BEGIN */
IF customer.balance > 0 THEN /* ...condition false... */
ASSIGN customer.flag = YES.
END. /* COMMIT of a transaction that */
/* never wrote anything */
The inactive-wrapper machinery already exists — TxWrapper.createTxWrapper creates wrappers that are not yet backed by a database transaction, and maybeActivateTxWrapper starts the real one later. Only the trigger is wrong. Today it fires from BufferManager.openScope (:2312), BufferManager.openScopeAt (:2658), RecordBuffer.initialize (:7778, :7923) and PersistenceInvocationHandler.invoke (:108).
The cost of a needless transaction is a BEGIN/COMMIT pair per block and — the expensive part on PostgreSQL — a snapshot held open for the whole duration of the block, holding back xmin and vacuum. Long read-only REPEAT loops are the worst case.
2. Proposed change¶
Move the activation trigger to first record mutation, per permanent database. The _temp database keeps the current buffer-scope trigger.
Two hooks, because one point is not sufficient:
- Primary (early). At the point a DMO transitions to "will be written" — record create, record delete, and the first field assignment — activate the wrapper for that DMO's database. This must run before
BaseRecord's undoable-tracking call toSession.trackUndoable(BaseRecord.java:1849), becauseSavepointManager.trackUndoable(:485) asserts that the savepoint manager is already bound to a session. - Backstop (late). In
Persister— the single class that issuesINSERT/UPDATE/DELETESQL (insert,bulkInsert,bulkCopy,deletex2,update) — activate if not already active, and log when this path fires. The backstop firing means the primary hook missed a case; the log is how those cases are found, rather than shipping a silent write outside the transaction.
PersistenceInvocationHandler.invoke (the unsafe / direct-SQL proxy) keeps its unconditional activation — it cannot distinguish reads from writes.
3. Record lock bookkeeping must be decoupled from activation¶
Record locks are FWD-managed (LockManager), not database row locks. RecordLockContext.Perm defers release and downgrade while bufferManager.isTransaction() is true (RecordLockContext.java:504, :515), and the deferred release happens in RecordLockContext.transactionEnded() — called only from TxWrapper.end(), which TxWrapper.finished() skips for an inactive wrapper (TxWrapper.java:271-281).
Today that is unreachable: acquiring a lock requires a buffer, and opening a buffer scope activates the wrapper. After this change it becomes reachable — FIND ... EXCLUSIVE-LOCK with no subsequent write leaves the wrapper inactive and leaks the lock past the end of the transaction.
Proposed fix: split end() into a database half and a bookkeeping half. The bookkeeping half — RecordLockContext.transactionEnded() and the reclaimPendingKeys call that consumes its result — runs unconditionally in finished(), iterate() and retry(), whether or not the wrapper was ever activated. iterate() and retry() currently return early when inactive (TxWrapper.java:205-208, :227-230) and need the same treatment, otherwise an iterating read-only transaction block leaks locks at every iteration boundary.
4. Re-arming the deferral on iteration¶
TxWrapper.iterate() currently does end(); begin();. Once a REPEAT loop activates on one iteration, every later iteration carries a database transaction whether or not it writes. Since REPEAT with conditional DML is precisely the pattern under discussion, iterate() and retry() should instead deactivate: end the transaction, clear persistenceCtx, and return the wrapper to inactiveTxWrappers so the next iteration defers again. Session.commit already discards the savepoint manager, so re-installation on the next activate() is clean.
5. What does not change¶
TRANSACTION, _Trans, the TransactionManager transaction level and nesting, variable and temp-table UNDO, DirtyShareContext publication (there is nothing to publish without a mutation), and sequences (nextval is non-transactional on PostgreSQL regardless).
Reads in the pre-activation prefix run outside the transaction. Under READ COMMITTED that is a per-statement snapshot either way, so there is no visible difference; and an FWD session that intends to write holds an FWD exclusive lock, which already serialises FWD writers across the read-to-write window.
6. Configuration¶
persistence/defer-transaction-until-dml in the directory, read at bootstrap through Utils.getDirectoryNodeBoolean alongside force-no-undo-temp-tables (DatabaseManager.java:1789), defaulting to true. Setting it false restores the buffer-scope trigger, so a site that hits a regression reverts by configuration rather than by build.
7. Testing¶
A testcases battery covering: a read-only transaction block issues no BEGIN; conditional DML issues nothing on the false path and commits correctly on the true path; FIND ... EXCLUSIVE-LOCK with no write releases its lock at transaction end (the section 3 regression); nested sub-transaction blocks where the write happens in the innermost block; REPEAT writing on alternate iterations; UNDO both before and after the first DML; and multi-database blocks where only one database is written. Transaction counts asserted from TxWrapper instrumentation, lock state asserted cross-session through the two-session harness, and OpenEdge baselines captured for anything where 4GL semantics are in question.TxWrapper instrumentation, lock state asserted cross-session through the two-session harness, and OpenEdge baselines captured for anything where 4GL semantics are in question.
Risks¶
- Silent write outside a transaction. This is the severe one. If a write path bypasses both the primary hook and the
Persisterbackstop, it executes under autocommit and is committed immediately.UNDOthen cannot roll it back and the failure is silent — no exception, just data that should not be there. The backstop and its logging exist specifically to bound this risk, but the risk is only as good as the enumeration of write paths. - Record lock leak past transaction end. Described in section 3. It is a genuine new regression introduced by the change, not a pre-existing one, and it is reachable from ordinary code (
FIND ... EXCLUSIVE-LOCKwith no write). - Ad-hoc transaction collision.
Persistence.Context.beginTransactionreturns false when a transaction is already open on the session, andTxWrapper.begin()throwsIllegalStateExceptionin that case unlessinitialTxis set. Deferring activation moves the window in which an ad-hoc transaction (for example theTEMP_CTXtransactions inTemporaryBuffer, or a trigger touching another database) can be open when the master wrapper finally activates. - Error timing shift. A connection or transaction failure now surfaces at the first DML rather than at block entry. Code that expects to fail early — and any
ON ERRORphrase scoped to catch it — sees the error at a different point in the block. - Read-then-write staleness against non-FWD writers. A record read before activation and updated after it was read outside the transaction. FWD's own lock manager serialises FWD sessions, but an external writer against the same database has a wider window than before.
- Savepoint manager re-installation. Section 4 deactivates and reactivates wrappers across iterations.
Session.beginTransactionthrows if a savepoint manager is already installed, so the deactivation path has to leave the session genuinely clean; getting this subtly wrong produces anIllegalStateExceptiononly on loops that write on some iterations and not others. - Reduced observability. Anything that samples database transaction state — VSTs, monitoring, statement logs, support diagnostics — will report substantially fewer transactions. Existing dashboards and any tooling that infers activity from transaction counts will need to be re-read.
Open questions¶
- Should section 4 (re-arming the deferral per iteration) land in this branch, or ship separately after sections 2, 3 and 6 have soaked? It carries the savepoint-manager risk above and is separable.
- Is there any reason an
EXCLUSIVE-LOCKread with no mutation needs a database transaction that I have not found? The lock manager is in-memory, but a dialect-specific escalation path would invalidate the premise. - Should the
Persisterbackstop log, or fail fast in non-production builds? Failing fast finds missed write paths during testing; logging avoids turning a missed path into an abend at a customer. - Is the enumeration of write paths in section 2 complete — specifically, do audit record generation, database write triggers, and bulk/dynamic temp-table operations reach
Persister, or do any of them reach the database another way?
Alex, please glance over the design and comment on the risks and open questions, thanks.
#4 Updated by Alexandru Lungu about 2 months ago
I am not sure whether Session.beginTransaction is something to be deferred at all. My point is that there is no BEGIN TRANSACTION SQL being run to the database, so starting a transaction even though no changes will be done is a no-op. My big concern is with the actual commit that is an actual SQL. I think delaying the transaction begin would be more complicated than using a flag for the first actual change done in that bracket.
I think the design is targeting an upper layer, including record locks iteration, savepoint manager. I don't think that any of these are relevant for the cause of simply avoiding the COMMIT SQL to save performance. Managing this at a higher level can help us allocate less memory resources (TxWrapper instances and so on), but the optimization will be marginal.
I would like to scope this to the orm package and don't let the user (i.e. 4GL compatibility layer) plug into it.
A phase 1 of this task will be to abstract any JDBC connection through session if possible. Thus, Session will always know the database state at a moment of time. Arbitrary SQL run by direct-Java access is accounted as "drop any optimization chances".
A phase 2 would be to hook in the right API of the Session to detect if the ORM session actually changed the DB in any way, in order to make commit a no-op eventually.
#5 Updated by Artur Școlnic about 2 months ago
- Status changed from WIP to Review
- reviewer Alexandru Lungu, Ovidiu Maxiniuc added
Summary of the changes¶
The change defers the SQL transaction until the ORM session actually writes, so a read-only transaction issues no commit at all.
Only the database transaction is affected. The change is confined to com.goldencode.p2j.persist.orm; TransactionManager, BlockManager, TxWrapper, BufferManager and RecordLockContext are untouched, so 4GL transaction scoping, the TRANSACTION function, _Trans and record-lock lifetimes are unchanged by construction.
Mechanism¶
Session now distinguishes two states: inTx (a transaction has been requested — unchanged semantics, still what inTransaction() returns) and txMaterialized (the JDBC connection has actually left auto-commit).
beginTransactionno longer clears auto-commit; it only sets the logical flag.clearAutoCommitis the single point where the connection leaves auto-commit. It is driven fromcheckTx(which every DML entry point already called),setSavepoint,getConnection, and the newprepareForStreaming.commit/rollbackskip the SQL entirely when nothing was ever materialized. This is the saving.- Auto-commit is restored when a transaction ends, so the next transaction on that session can defer in its turn — without this the connection stays in transaction mode for the rest of the session's life and every later commit is genuinely owed.
Two connection accessors now exist: getConnection() materializes (the safe default for callers whose intent is unknown), and getConnectionForRead() does not. The hot read paths — record load, extent hydration, unique/PK probes, and the non-streaming query paths — use the read accessor; everything else keeps the materializing one.
Gated by persistence/defer-transaction-until-dml in the directory, default on.
Worth knowing¶
The saving is narrower than the ~550k commits/run measured in #10275 suggest. Savepoint creation materializes at the first in-memory DMO change, and scrolled queries must materialize to keep a server-side cursor, so elision only survives for transactions whose reads are all FIND / record hydration and which assign nothing. FOR EACH and OPEN QUERY do not qualify.
The subtle areas, all of which came out of review and are the places worth the most attention:
- Auto-commit is restored on the success path only —
setAutoCommit(true)commits a transaction in progress, so restoring it after a failed rollback would commit the very work the caller was told was discarded. prepareForStreamingclears auto-commit even with no transaction open, because a JDBC fetch size only yields a server-side cursor outside auto-commit and the common case for a large read is no transaction at all.beginTransactionclears auto-commit before mutating any session state, so a failure leaves the session exactly as it was.MetadataManager.removeDynamicTablenow begins its transaction before taking the connection; the previous order left its deletes outside the transaction.
Testing¶
A new slice, tests/persistence/lazy_transaction — 125 ABLUnit tests in 11 classes. The patterns covered:
- read-only transaction blocks, and the conditional-DML pattern this work targets, on both branches;
- every distinct form of "first write" — assign, create, delete, validation-forced flush, write trigger, buffer-copy, dynamic buffer, sequence draw, and a write issued from a callee;
- reading-query shapes, both read-only and reading back the session's own uncommitted write, since query shape now determines whether a transaction materializes;
- iterating transaction blocks that write on only some iterations;
- transactions spanning internal, external and persistent procedure calls;
- error and rollback paths either side of the first write, including constraint violations raised by the flush itself, RETRY, labelled UNDO and CATCH;
- record locking with no write, and lock upgrade;
- temp-table and permanent-table writes in one block;
- the
TRANSACTIONfunction sampled across the materialization point; - long sequences of alternating read-only and writing transactions on one session.
Results¶
| Run | Pass | Fail |
|---|---|---|
| OpenEdge 12.8 | 125 | 0 |
| FWD trunk (16758) | 118 | 7 |
| FWD with change, deferral on | 119 | 6 |
| FWD with change, deferral off | 119 | 6 |
Ovidiu and Alex, please look over the changes so far before we move further with the design, the code is in 11884a.
#6 Updated by Artur Școlnic about 1 month ago
- Status changed from Review to WIP
Will do a few more rounds of self review and generate more tests.
#7 Updated by Artur Școlnic about 1 month ago
- Status changed from WIP to Review
The following issues have been solved:
Restrict prepareForStreaming to the only case that benefits from it — a forward-only cursor on a deferral-enabled session — so it no longer flips auto-commit on a temp database's shared connection or defeats the configuration switch, and correct the two javadoc comments that understated what the read accessor and the streaming residue actually do.
Tests committed to tests/persistence/lazy_transaction.
Please review rev 16764.
#8 Updated by Eric Faulhaber about 1 month ago
Please update % done based on where you estimate the development of this solution is currently.
#9 Updated by Artur Școlnic about 1 month ago
- % Done changed from 0 to 80
I am leaving some headroom for potential issues that can be found during review.
#10 Updated by Ovidiu Maxiniuc about 1 month ago
Review of 11884a / r16764
SavepointManager.java- line 56:
017is the correct H entry number; Please update the one from line 55 to16;
- line 56:
Session.java- lines 469-470: two extra empty lines;
- lines 1857: the savepoints should be stacked if the transaction was not materialised. Consider the following code:
do transaction: // begin transaction, no materialisation do transaction: // set savepoint, materialisation forces transaction to materialise if (false-condition) bufferX.fieldY = 10. // never executes, leaving the transaction 'empty' end. // release savepoint // another non-empty sub-transaction may exist here which could materialise the transaction end. // commit of empty transactionThe difficulty comes the fact that this method returns aSavepoint. But it can be a 'lazy' one. - lines 1975, 1945, see above. If the materialisation did not happen, the lazy savepoint proxy should be dropped from the stack;
- lines 2544-2573: package-private method
prepareForStreaming()should be moved fromprivatesection up the file;
SQLQuery.java- line 1343: (existing error in trunk (since 2023) noticed while looking at the changes, but please fix it) this method (
resetErrorHandler()) should delegate toDialect.resetErrorHandler(), notDialect.activateErrorHandler()- probably a copy/paste error.
- line 1343: (existing error in trunk (since 2023) noticed while looking at the changes, but please fix it) this method (
- in
BlobType.readProperty()andClobType.readProperty(), the connection is always acquired usinggetConnection()which will cause the transaction to materialise, only tosetAutoCommit(false)immediately. This should happen conditionally (if (isAutoCommit) ...). Of course, the restore should also be conditioned.
#11 Updated by Artur Școlnic about 1 month ago
- Status changed from Review to WIP
Thank you, Ovidiu, will get on it.
#12 Updated by Artur Școlnic about 1 month ago
- Status changed from WIP to Review
Savepoint stacking (1857, 1945, 1975), I could not reproduce.
In your example the inner do transaction: never sets a savepoint, so nothing materialises. Each step on the way to Session.setSavepoint has exactly one caller:
Session.setSavepoint <- SavepointManager.setSavepoint(Block) only caller: line 571 <- SavepointManager.lazilySetSavepoints only caller: line 494 <- SavepointManager.trackUndoable(dmo) <- Session.trackUndoable <- BaseRecord.prepareChangeSet <- dataChanged | bulkDataChanged | updateState
All three leaves are DMO mutations, so a sub-transaction that changes nothing never gets there. The other direct callers Validation.flushImpl, TemporaryBuffer under if (doSave), FastCopyHelper, JsonImport are all write paths, and Validation:1590 is commented out.
1945 and 1975 follow: setSavepoint materialises before returning, so a non-null Savepoint always implies txMaterialized and the early-out is unreachable while one is outstanding. No lazy proxy to drop.
Added two tests to TestConditionalDml (emptyNestedSubTransactionWritesNothing, writingSubTransactionAfterAnEmptyOnePersists); both pass. They pin the visible outcome only the battery cannot see materialisation so the call graph is the real evidence.
Addressed the other points.
#13 Updated by Ovidiu Maxiniuc about 1 month ago
- % Done changed from 80 to 100
- Status changed from Review to Internal Test
I am OK with the changes in 11884a/r16765. The branch is ready for tests.
#14 Updated by Alexandru Lungu about 1 month ago
- Status changed from Internal Test to Review
Please let me do a second pass for review. I will manage to finish it Monday EOD.
#15 Updated by Artur Școlnic 26 days ago
Tested #10275, the commit statements were reduced 10x, from ~340k to ~34k. Also did some regression testing with the multi tenant app, it passed.
#17 Updated by Alexandru Lungu 20 days ago
AI-based review:
- [MAJOR] functional
Session.commit:commitnow restores auto-commit viaresetAutoCommit, which breaks the ANALYZE step ofImportWorker.runon the PostgreSQL bulk-copy path (the default import path, since onlyP2JPostgreSQLDialectsupportsBulkCopy). The import session doesbeginTransaction/bulkCopy/commit(ImportWorker ~1747-1783) and stays open; the ANALYZE block (~2073-2087) takessession.getConnection()(no materialization,inTxis false), runsanalyze <table>and callsconnection.commit()directly, which pgjdbc 42.7.8 rejects with "Cannot commit when autoCommit is enabled."; the catch'sconnection.rollback()throws likewise and escapes to the outercatch (Exception)(~2100). Every non-empty table is logged SEVERE "Error processing import data" and added tofatalalthough its rows were committed. Previously the connection stayed in manual-commit mode aftercommit, so this worked. Fix inImportWorker(skip commit/rollback whengetAutoCommit()is true, or bracket ANALYZE withsession.beginTransaction()/session.commit()).
DB import thus should be regression tested, as well as other processes that we have external to the runtime that involve the DB.
- [MAJOR] performance
BlobType.readProperty: (alsoClobType.readProperty) Previously a (reclaimed, long-lived) session's connection stayed out of auto-commit after its first transaction, so thesetAutoCommit(false)/ restore around the LOB read were driver no-ops. NowresetAutoCommitrestores auto-commit after every commit/rollback and deferral keeps it on until first DML, so in the window after a commit/rollback until the next DML, savepoint or forward-only query, every non-streaming read (SQLQuery.list/uniqueResult, paged list,Loader, scroll-insensitive) of a BLOB/CLOB field toggles the mode per LOB cell per row: two round trips per cell on SQL Server (set implicit_transactions) and MariaDB (set autocommit), one extra COMMIT per non-null blob on PostgreSQL. E.g.FIND tbl ... NO-LOCKor a 100-row preselect list with two LOB columns (~400 extra round trips on SQL Server/MariaDB).
To be investigated.
- [MINOR] performance
Session.commit: (alsorollback, viaclearAutoCommit/resetAutoCommit) every transaction that materializes now flips auto-commit off and back on; on SQL Server (mssql-jdbc 12.10) and MariaDB (Connector/J 3.5.3) each flip is a server round trip, so a materialized transaction costs 3 round trips instead of 1 (pgjdbc flips locally, no extra cost). Any DML materializes; on MariaDB a non-scrolling FOR EACH also does viaprepareForStreaming(on SQL ServersupportsJdbcApi(API_LAST_ON_FORWARD_ONLY)is false so ordinary FOR EACH does not stream). The deferral pays off on these dialects only if fewer than about one in three transactions materialize; worth measuring before enablingdefer-transaction-until-dmlby default there. - [MINOR] functional
BlobType.readProperty: (alsoClobType.readProperty) the new comment says the guard is needed "because restoring it would commit whatever that transaction was in the middle of". That is incorrect: with auto-commit already off the old restore wassetAutoCommit(false), a no-op per JDBC and on pgjdbc, mssql-jdbc, MariaDB Connector/J and H2 (all return early on an unchanged mode). Reword the comment: the guard only avoids a redundant driver call.
#18 Updated by Alexandru Lungu 20 days ago
I see many of the changes are around the setAutoCommit(false) logic. I am not sure if this is something that is still relevant for FWD.
As long as FWD uses a connection pool, once setAutoCommit(false) is used, that connection will always be auto-commit false, even when checked-out. In other words, as long as we don't connect to the database with auto-commit set, this setAutoCommit(false) seems to be just a "lazy initialization" of the connection. In most cases, this is redundant as the connection is already non-auto-commit (e.g. when checked out of c3p0).
Artur, please document the use-cases of FWD when auto-commit is desired to be true. I am inclined to make false default and enable it when needed instead of enabling it by default and always taking care to turn it off. Something like a c3p0 configuration "on connection established" to set it to auto-commit false always.
#19 Updated by Artur Școlnic 20 days ago
- % Done changed from 100 to 90
- Status changed from Review to WIP
As discussed in the daily, I will go through our code base and make sure we don't rely on the autocommit being true, if that is the case, I will set it to false to all c3p0 managed connections and remove setAutoCommit(false) from the codebase. Also will go through review points.
#20 Updated by Alexandru Lungu 20 days ago
Artur Școlnic wrote:
we don't rely on the autocommit being true,
- either we set autocommit to true deliberatelly.
- or we get a connection and don't do anything with the autocommit -> this presumes that it will use the true default.
*this is buggy, because once another session checks out the connection, sets it autocommit to false and checks it back in, then the "default" becomes false for *some transactions.
#21 Updated by Artur Școlnic 19 days ago
Alex, to answer your initial question, the autocommit is largely not used in FWD, but it is essential for this branch.
Trunk used one boolean for two different things. The branch splits them:
- inTx — a transaction has been requested. Unchanged timing; still what TRANSACTION returns.
- txMaterialized — the JDBC connection has actually left auto-commit.
Lifecycle
1. beginTransaction sets inTx and touches no JDBC state. The connection stays in auto-commit.
2. Reads in the prefix are therefore implicit single-statement transactions — nothing held open, nothing owed.
3. The first write reaches checkTx() → clearAutoCommit(), the single place the connection leaves auto-commit and the point txMaterialized is set. The other materialization points are setSavepoint (a savepoint cannot exist without a transaction behind it), getConnection() (caller's intent unknown), and prepareForStreaming.
4. commit() / rollback() skip the SQL entirely when txMaterialized == false, then resetAutoCommit() restores auto-commit so the next transaction on that session can defer in its turn.
#22 Updated by Alexandru Lungu 19 days ago
Lifecycle
Completely agreed. This was my understanding as well on your changes.
But, I don't understand why we need to so much work around the auto-commit when it is not used by FWD. I prefer to remove it entirely (and move it to c3p0 connection setup time). This way, the changes in #11884 won't need to deal with auto-commit at all.
Alex, to answer your initial question, the autocommit is largely not used in FWD, but it is essential for this branch.
I don't see where this "essential" is. What would happen if 11884a changes will not touch auto-commit at all?
#23 Updated by Artur Școlnic 19 days ago
Alexandru Lungu wrote:
I don't see where this "essential" is. What would happen if 11884a changes will not touch auto-commit at all?
The saving is "don't send COMMIT", and that is only safe if no transaction was ever opened, in JDBC, "no transaction is open" is auto-commit mode. So the auto-commit handling isn't wrapping around the optimization; it is the optimization.
#24 Updated by Alexandru Lungu 19 days ago
The saving is "don't send COMMIT", and that is only safe if no transaction was ever opened,
I agree
in JDBC, "no transaction is open" is auto-commit mode.
I am not sure what you mean by this.
- If auto-commit is set to true, a transaction is open if you send
BEGIN TRANSACTIONand ends onCOMMIT/ROLLBACK - If auto-commit is set to false, a transaction is open automatically at first DML.
So the auto-commit handling isn't wrapping around the optimization; it is the optimization.
There is a decision to be made if FWD uses either explicit BEGIN in auto-commit true or implicit BEGIN on first DML in auto-commit false.
As I am inclined to say the second is the one adopted by FWD, I can't tell why we need to set auto-commit for each transaction, instead of only once at the connection set-up.
#25 Updated by Artur Școlnic 19 days ago
Alexandru Lungu wrote:
- If auto-commit is set to true, a transaction is open if you send
BEGIN TRANSACTIONand ends onCOMMIT/ROLLBACK- If auto-commit is set to false, a transaction is open automatically at first DML.
With auto-commit false, PostgreSQL opens the transaction at the first statement, not the first DML. I could retest, but this was my conclusion from prior probes.
#26 Updated by Alexandru Lungu 19 days ago
With auto-commit false, PostgreSQL opens the transaction at the first statement, not the first DML.
Now that I think about it, does it even matter? Even if the transaction is scoped larger, as long as there is no DML, the rollback/commit is a no-op for these.
I could retest, but this was my conclusion from prior probes.
I am not sure how you can test this considering the mention above. If you rollback, all DMLs will be reverted. However, how could you know if the queries done before the DMLs were captured by the transaction as well.
#27 Updated by Artur Școlnic 19 days ago
The transaction starts at the DML because FWD toggles the flag there. Nothing about auto-commit=false defers to DML; it opens on the first statement of any kind. FWD gets DML timing by staying in auto-commit until the write, then switching.
#28 Updated by Alexandru Lungu 19 days ago
Now that I think about it, does it even matter? Even if the transaction is scoped larger, as long as there is no DML, the rollback/commit is a no-op for these.
Maybe it does if the connection is question does only queries. In this case, no COMMIT will occur so the transaction will be left opened. But this also happens in trunk right? Unless we start it in auto-commit true and on first DML we switch it to auto-commit false till the commit. After commit, we should switch it back to auto-commit true so that upcoming queries do not start long running transactions.
#29 Updated by Alexandru Lungu 19 days ago
- Query.getDialect(session).activateErrorHandler(session.getConnection()); + Query.getDialect(session).resetErrorHandler(session.getConnectionForRead());
- This looks like an unrelated change.
Otherwise, I am ok with the changes
#30 Updated by Artur Școlnic 19 days ago
Alexandru Lungu wrote:
- This looks like an unrelated change.
It's a quick fix requested in note 10, but yes, unrelated.
#31 Updated by Alexandru Lungu 19 days ago
- Status changed from WIP to Internal Test
- % Done changed from 90 to 100
Ok, lets keep it.
#32 Updated by Artur Școlnic 19 days ago
- % Done changed from 100 to 90
#33 Updated by Artur Școlnic 18 days ago
- % Done changed from 90 to 100
#34 Updated by Artur Școlnic 15 days ago
The following testing was done and passed:
Multi tenant app - harness, reports, webui navigation, convlaunvch
ETF - full testing suite
Chui regression tests
Large gui app unit tests
#35 Updated by Artur Școlnic 14 days ago
| Suite | Tests | Pass | Fail |
|---|---|---|---|
persistence/lazy_transaction |
187 | 183 | 4 |
persistence/flushing_validation |
3493 | 3058 | 435 |
flushing_validation/query_shape_uncommitted_visibility |
185 | 123 | 62 |
flushing_validation/presort_review_findings |
66 | 53 | 13 |
the results with trunk are the same.
#36 Updated by Artur Școlnic 13 days ago
I think we can merge 11884a.
#37 Updated by Alexandru Lungu 12 days ago
I think we can merge 11884a.
Please merge 11884a to trunk now.
Afterwards, please assess each customer checking whether they use direct Java access. If they do, open a separate task on their project to suggest changes to their native Java code or at least notify through that task this change.
#38 Updated by Alexandru Lungu 12 days ago
- Status changed from Internal Test to Merge Pending
#39 Updated by Artur Școlnic 12 days ago
- Status changed from Merge Pending to Test
11884a was merged to trunk 16812.