Bug #11573
Remove unnecessary JDBC savepoint during validation
90%
History
#1 Updated by Artur Școlnic 26 days ago
Profiling a client's application write heavy scenario showed that we are setting and immediately releasing savepoints during validation.
In this case, a record was already inserted and its NEW/dirty state was cleared. A later validation path still entered Validation.flushImpl(), created a savepoint, called Session.save(), and immediately released the savepoint, but Session.save() emitted no SQL. This produced an empty SAVEPOINT / RELEASE SAVEPOINT pair and incorrectly treated a no-op validation pass as a real flush.
I will change Validation.flush() so it only creates the validation savepoint when there is actual DML pending.
#2 Updated by Artur Școlnic 26 days ago
- Status changed from New to WIP
- Assignee set to Artur Școlnic
#4 Updated by Artur Școlnic 26 days ago
- % Done changed from 0 to 100
- Status changed from WIP to Review
I committed the solution to 11573a, in the end it was a corner case where a trigger flushed and validated a record, the validation continued for it (after trigger execution), setting the savepoints unnecessarily. Constantin, please review.
#5 Updated by Constantin Asofiei 26 days ago
- reviewer Constantin Asofiei, Ovidiu Maxiniuc added
Ovidiu, please also take a look. Changes make sense for me.
#6 Updated by Ovidiu Maxiniuc 26 days ago
My first question is what happens if the trigger decides to reject the record using RETURN ERROR? buffer.maybeFireWriteTrigger(); from line 634 would raise an exception without the savepoint being set.
The second is: if (!dmo.checkState(NEW) && !dmo.isDirty()) (record is unchanged from the version from SQL) do we still need to fire the trigger? Since _ record was already inserted_ the trigger should have been fired at that time. The comment at line 632 explain that the old mechanism relies on a side-effect for avoiding the double firing, but this condition seems like a better alternative.
So, from a theoretical standpoint, it seems to me that the block at 636-642 is correct, but the one at 632-634 should be moved back?
#7 Updated by Artur Școlnic 25 days ago
Ovidiu,
You are right, I think the fix is to first short circuit clean, already-persisted records. Then fire the trigger only for records that still have pending work. After the trigger returns, we check again, because the trigger may have flushed the record reentrantly.
Please review rev 16623.
#8 Updated by Alexandru Lungu 25 days ago
I will change Validation.flush() so it only creates the validation savepoint when there is actual DML pending.
This was added in some of Radu's changes long ago when "database validation" was added for _temp. Prior to his changes, there was no such "micro-savepoint", but it was added for safety. I don't recall if we found an actual bug that required this, but it was added because we feared having the database in an incorrect state if the insert failed. AFAIK, it was related to H2 temporary database. Prior to this changes, there was no _temp unique checks because indexes in _temp are not unique - this allows us to have inconsistencies in volatile data and do validation server-side. Due to performance drawbacks, we decided to add "allowDBUniqueCheck" and SOFT-UNIQUE indexes. This is a H2 customization that allowed us to toggle validation on and off. Thus, unique indexes will be throwing errors only when validation is toggled.
AFAIK, a failing INSERT would automatically rollback that operation, so the savepoint may be redundant even if there is a DML or not happening.
if (!dmo.checkState(NOUNDO))
{
// preemptively set lazy savepoints in the savepoint manager before we create a "micro" savepoint
// here, because otherwise, the save here will trigger the savepoint manager to lazily create them,
// and we do not want those savepoints nested within this one
SavepointManager savepointManager = session.getSavepointManager();
if (savepointManager != null)
{
// this is a no-op if we are not in a transaction or we are in a full transaction block
lastBlockSetup = savepointManager.lazilySetSavepoints(true);
}
}
This code in flush is setting savepoint anyway, so I guess this makes flushImpl savepoints always redundant.
#9 Updated by Alexandru Lungu 25 days ago
PS: My comment was to remind of the origin for this savepoint. Radu, do you recall the task in which this happened? The SOFT-UNIQUE implementation.
#10 Updated by Radu Apetrii 25 days ago
Alexandru Lungu wrote:
PS: My comment was to remind of the origin for this savepoint. Radu, do you recall the task in which this happened? The SOFT-UNIQUE implementation.
Yes yes, it was #7323. I don't really remember the particularities of that task, but I can look into it if needed.
#11 Updated by Artur Școlnic 22 days ago
Ovidiu, please take a look again at rev 16623.
#12 Updated by Artur Școlnic 22 days ago
- Status changed from Review to WIP
Ovidiu, I saw that we eagerly set savepoints for undoable tracking, I am trying to improve that, please postpone the review of 11573a.
#13 Updated by Artur Școlnic 22 days ago
- Status changed from WIP to Review
Ovidiu, Constantin,
The changes in rev 16624 are as follows:
Undoable DMO changes are still tracked immediately in memory.
Database savepoint creation is delayed until the first DML.
The first flush reuses that application savepoint instead of creating another micro-savepoint.
Later flushes still use separate micro-savepoints.
Please review.
#14 Updated by Ovidiu Maxiniuc 22 days ago
I have reviewed 11573a, 16622..16624. I think the solution might work.
Please fix the following code style issues:- the history numbers in
Session.java(026, 028->028, 029); - remove the
029(only the digits at start of line 81) fromValidation.javahistory (both028and029are part of this branch); - move
static final class DmlSavepointbeforeprivate static class BlockinSavepointManager.java
Constantin, I did not change the status of the task to allow you the chance to have a look over the changes.
#15 Updated by Artur Școlnic 21 days ago
Thank you, Ovidiu.
I will start testing while Constantin looks over the changes.
#16 Updated by Artur Școlnic 21 days ago
- Priority changed from Normal to Urgent
Changed priority to reflect the urgency of the fix.
#17 Updated by Constantin Asofiei 21 days ago
Artur, do you have a standalone test which shows how these changes are used?
#18 Updated by Artur Școlnic 21 days ago
No, I will come up with one.
#19 Updated by Artur Școlnic 21 days ago
start.p
define variable iSourceBookId as integer initial 2 no-undo.
define variable iTargetBookId as integer initial 3 no-undo.
define variable iCopied as integer no-undo.
define buffer b-book for Book.
for each Book exclusive-lock:
delete Book.
end.
do transaction:
create Book.
assign
Book.book-id = iSourceBookId
Book.isbn = "ISBN0001"
Book.book-title = "title1".
create Book.
assign
Book.book-id = iSourceBookId
Book.isbn = "ISBN0002"
Book.book-title = "title2".
end.
for each Book where
Book.book-id = iSourceBookId
no-lock:
do for b-book transaction:
create b-book.
buffer-copy Book except book-id rowpointer dttz to b-book
assign
b-book.book-id = iTargetBookId
iCopied = iCopied + 1.
end.
end.
message "Created Book records:" iCopied.
w_book.p
trigger procedure for write of Book old buffer ob-book.
if (if Book.rowpointer = ? then "" else Book.rowpointer) = "" then
Book.rowpointer = guid.
Book.dttz = now.
book.df
ADD TABLE "Book" AREA "Employee" DUMP-NAME "book" TABLE-TRIGGER "Write" NO-OVERRIDE PROCEDURE "w_book.p" CRC "?" ADD FIELD "book-id" OF "Book" AS integer FORMAT "->,>>>,>>9" INITIAL "0" POSITION 2 MAX-WIDTH 4 ORDER 10 ADD FIELD "book-title" OF "Book" AS character FORMAT "x(8)" INITIAL "" POSITION 3 MAX-WIDTH 16 ORDER 20 ADD FIELD "isbn" OF "Book" AS character FORMAT "x(8)" INITIAL "" POSITION 4 MAX-WIDTH 16 ORDER 30 MANDATORY ADD FIELD "new_dt" OF "Book" AS character FORMAT "x(8)" INITIAL "" POSITION 6 MAX-WIDTH 16 ORDER 40 ADD FIELD "rowpointer" OF "Book" AS character FORMAT "x(36)" INITIAL ? POSITION 5 MAX-WIDTH 72 ORDER 45 ADD FIELD "ext" OF "Book" AS integer FORMAT "->,>>>,>>9" INITIAL "0" POSITION 7 MAX-WIDTH 66 EXTENT 3 ORDER 50 ADD FIELD "dttz" OF "Book" AS datetime-tz FORMAT "99/99/9999 HH:MM:SS.SSS+HH:MM" INITIAL ? POSITION 8 MAX-WIDTH 12 ORDER 60 ADD FIELD "dt" OF "Book" AS date FORMAT "99/99/99" INITIAL ? POSITION 9 MAX-WIDTH 4 ORDER 70 ADD FIELD "dtt" OF "Book" AS datetime FORMAT "99/99/9999 HH:MM:SS.SSS" INITIAL ? POSITION 10 MAX-WIDTH 8 ORDER 80 ADD INDEX "idx" ON "Book" AREA "Employee" UNIQUE PRIMARY INDEX-FIELD "book-id" ASCENDING INDEX-FIELD "isbn" ASCENDING
With trunk, a savepoint is set by track undoable in the trigger procedure, on rowpointer set, then a savepoint is set on exiting the trigger procedure which flushes the record, then one savepoint is set in flushImpl after the trigger exits.
With 11573a just one savepoint is set by track undoable, then it is used by the first DML only.
#20 Updated by Artur Școlnic 21 days ago
Can I continue testing?
#21 Updated by Alexandru Lungu 21 days ago
Artur, some AI observations:
- [MAJOR] functional
Validation.flush:dmlSavepoint.complete()runs after thetry { flushImpl(...) } finally { nursery.remove(...) }block (Validation.java:663-678). WhenSavepointManager.prepareDmlSavepointtakes itsactiveChange falsepath it eagerly sets the parent savepoints vialazilySetSavepoints(true)but defers the inner active block's savepoint into theafterDmllambda held by the returnedDmlSavepoint(SavepointManager.java:524-532, 585-592). IfflushImplthrows (validation failure, persistence error, or aConditionExceptionfrom a nested WRITE-trigger DML),complete()is skipped,activeBlock.savepointstaysnull, and a subsequentUNDOexiting the inner block rolls back to the parent block's boundary instead of one that excludes the failed DML — breaking the 4GL nested-block atomicity contract. Movecomplete()inside thetry(right afterflushImpl) or into a dedicated success-only branch offinally. - [MAJOR] functional
Session.save: in the privatesave(dmo, updateDmoState, dmlPrepared)methodpersister.insert/persister.update(Session.java:1048/1053) is invoked betweenprepareDmlSavepoint(line 1042) and the unconditionaldmlSavepoint.complete()(line 1080) with notry/finally. WhenprepareDmlSavepointtook theactiveChange falsepath, the inner block's savepoint setup is deferred intoDmlSavepoint.afterDmland only realized bycomplete(). A persister failure — the very case the savepoint is meant to protect against — skipscomplete(), leaving the more-deeply-nested block without its boundary savepoint while the parent's is in place: the same asymmetric rollback-boundary defect asValidation.flush. Wrap the persister call socomplete()runs on success only. - [MINOR] functional
SavepointManager.prepareDmlSavepoint: whenactiveBlock.tracks(dmo)is false the code unconditionally selectsparentBlockas the rollback-owning block (SavepointManager.java:525).Block.tracksdoes not walk the block stack andtrackUndoableonly writes to the currentactiveBlock, so a DMO can legitimately be tracked in a grandparentGwhile bothactiveBlockAandparentBlockPreturn false fromtracks(dmo)— any sub-block entered betweenGand the DML without itself touching the DMO produces this.lazilySetSavepointsthen creates a fresh savepoint atP, so the DML is bracketed byP's savepoint instead ofG's; ifP's sub-transaction rolls back, the DB write is undone whileG.changedstill records the in-memory change, diverging in-memory state from the database. Walk the block stack to locate the true owning block (or roll the tracking forward) instead of hard-codingparentBlock. - [MINOR] style
Validation.flush: blank line before closing}of method body; style rule forbids blank lines before}. - [MINOR] style
Validation: history entry029is the second entry added in this diff but carries a sequence ID; per project convention only the first new entry in a diff gets the sequence number, subsequent entries in the same diff have no ID. - [MINOR] performance
Validation.flush:dmo.getDirtyProps().clone()is invoked twice in the new short-circuit logic (Validation.java:634, 644). The pre-trigger clone at line 634 is dead in the common NEW/dirty path becausefinishIfAlreadyFlushed(line 696) returnsfalsewithout consuming the parameter. Cost is small (BitSet of ~5-50 bits = 1-2long@s), but the clone could be deferred — inline the @NEW || isDirty()check and only clone when the snapshot is actually needed fornursery.remove.
#22 Updated by Artur Școlnic 21 days ago
Alex,
If you agree with those points, I will fix the issues AI found.
#23 Updated by Artur Școlnic 21 days ago
Sorry, that was supposed to be a question :)
Should i address those findings?
#24 Updated by Constantin Asofiei 21 days ago
Artur Școlnic wrote:
Yes, but in the mean time, please test:Can I continue testing?
- what happens if you remove the trigger
- what happens if what you do in the trigger is done in an inner
RUN proc0call (without the trigger).
#25 Updated by Alexandru Lungu 21 days ago
Should i address those findings?
Please cross-check them. I will do a separate individual review my own. If you think any of the points provided is out of place, just let me know, I will double-check that.
#26 Updated by Artur Școlnic 21 days ago
Validation.flush
The described failure path is not quite right, when flushImpl fails, it rolls back its own savepoint, so the missing inner savepoint does not cause the claimed parent rollback. However, complete() should still run immediately after a successful flushImpl, because the later nursery cleanup can throw and prevent the savepoint boundary from being completed.
Session.save
If insert or update fails, the deferred savepoint should not be completed because no write succeeded. The real risk is that the database write succeeds, but later code throws before complete() runs. Calling complete() immediately after a successful insert or update would avoid that problem.
SavepointManager ownership
The changed record may belong to a block higher than the immediate parent. Always selecting parentBlock can put the database savepoint at the wrong nesting level. Rolling back an intermediate block could then undo the database write while leaving the record's in-memory state unchanged. We should locate the block that actually tracks the record.
The other 3 points are trivial.
I addressed the review in rev 16626.
#27 Updated by Artur Școlnic 21 days ago
Constantin Asofiei wrote:
in the mean time, please test:
- what happens if you remove the trigger
Only one savepoint is set, during the flush in do transaction where the actual insert is happening
- what happens if what you do in the trigger is done in an inner
RUN proc0call (without the trigger).
The procedure cannot update the record since the buffer has a no-lock. Seems like the triggers don't need an exclusive lock or are acquiring one by default.
#28 Updated by Constantin Asofiei 21 days ago
Artur Școlnic wrote:
The procedure cannot update the record since the buffer has a no-lock. Seems like the triggers don't need an exclusive lock or are acquiring one by default.
I do not understand. b-book is an explicit buffer and defined in the external procedure, and you should be able to modify it in a proc0.
#29 Updated by Artur Școlnic 21 days ago
I used an inner procedure. Will test with an external one.
#30 Updated by Artur Școlnic 21 days ago
Oh, I was using the implicit buffer for the assign, not b-book.
Anyway, the behavior is the same, there is only one savepoint set, before the insert in the do transaction, the procedure does not affect flushing.
#31 Updated by Constantin Asofiei 21 days ago
Artur Școlnic wrote:
Oh, I was using the implicit buffer for the assign, not b-book.
Anyway, the behavior is the same, there is only one savepoint set, before the insert in thedo transaction, the procedure does not affect flushing.
- What if you do a DO TRANSACTION (a sub-tx) in
proc0? - what if you pass this buffer to another external procedure?
#32 Updated by Artur Școlnic 21 days ago
Constantin Asofiei wrote:
- What if you do a DO TRANSACTION (a sub-tx) in
proc0?
no effect, just one savepoint set in the full transaction do block.
- what if you pass this buffer to another external procedure?
only one savepoint, set by the undoable tracking and reused by the flush.
#33 Updated by Artur Școlnic 21 days ago
Testing done for 11573a:
Multi tenant application: harness, performance and reports
Large gui application: unit tests (me), smoke tests (Stefanel)
Another customer application: smoke tests will be done by Razvan tomorrow morning.
#34 Updated by Artur Școlnic 20 days ago
Artur Școlnic wrote:
Another customer application: smoke tests will be done by Razvan tomorrow morning.
This one passed too. All testing is done.
#35 Updated by Constantin Asofiei 20 days ago
Artur Școlnic wrote:
Artur Școlnic wrote:
Another customer application: smoke tests will be done by Razvan tomorrow morning.
This one passed too. All testing is done.
Including ETF and ChUI?
#36 Updated by Artur Școlnic 20 days ago
Not, just the ones I mentioned. Should I continue with those?
#37 Updated by Constantin Asofiei 20 days ago
Artur Școlnic wrote:
Not, just the ones I mentioned. Should I continue with those?
Yes, please.
#38 Updated by Artur Școlnic 20 days ago
Chui regression tests passed, the only one left is ETF, which is tested by Teodor.
#39 Updated by Teodor Gorghe 20 days ago
There are some tests from ETF which has failed and these are unexpected.
Check devsrv01:/tmp/tg.20260701/11573.zip
#40 Updated by Artur Școlnic 19 days ago
Teodor,
Please send me in private the 4gl sources for the failing test.
#41 Updated by Teodor Gorghe 19 days ago
I still have not found the procedure because the test suite is large.
Please check the automated code review and see if is relevant:
Modified areas: com.goldencode.p2j.persist.orm (3 files: SavepointManager.java, Session.java, Validation.java)
Domains reviewed: fwd-persistence-layer
Functional regression¶
- [CRITICAL] functional
Validation.flush: The write trigger now fires before the block rollback boundary is established. In trunk,flush()calledsavepointManager.lazilySetSavepoints(true)at the very top — beforemaybeFireWriteTrigger()— with an explicit comment that block savepoints must be set up first so the flush's own savepoint is not nested inside them. In 11573a the order is inverted:maybeFireWriteTrigger()(Validation.java:641) runs first, and the block savepoint is only established later inprepareDmlSavepoint(Validation.java:658) / by the trigger's own DML. Any WRITE trigger that (a) performs its own undoable DML, or (b) raisesRETURN ERROR, now runs with a different savepoint nesting than trunk, so a subsequent block UNDO rolls back a different set of operations. This is the ordering Ovidiu flagged in review; it is the most likely cause of the ETF regression. - [MAJOR] functional
Session.save/Validation.flush: The deferred deeper-block savepoints (DmlSavepoint.afterDml) are only realized bycomplete(), which runs after the DML but is skipped on any exception thrown between the successful DML andcomplete()(e.g.nursery.remove, or a persister error in the privatesave). When skipped, a more-deeply-nested block is left without its boundary savepoint while the parent's is in place — an asymmetric rollback boundary. (Partially addressed by movingcomplete()next toflushImpl, but the window between a successful DML andcomplete()still exists.) - [MAJOR] functional
Block.rollback/SavepointManager.rollback: Removing thetxLevel > 0 && savepoint == nullearly-out means a block now performs in-memory rollback (dmo.rollback(txLevel), marking records STALE) even when no DB savepoint was ever set. This is required by the deferred model, but it changes rollback semantics for savepoint-less blocks and must be validated against the case where the DB write did happen under a different block's savepoint than the one tracking the record.
#42 Updated by Artur Școlnic 19 days ago
If this is going to be substantial effort on your part, I could setup ETF and test myself, please let me know.
#43 Updated by Teodor Gorghe 19 days ago
I think the first finding is relevant because if I run the tests manually, it passes. I think there is something related with a test which is expected to return an error, but leaves to a state which is unexpected, which leads to these three tests to fail.
#44 Updated by Teodor Gorghe 19 days ago
Artur Școlnic wrote:
I could setup ETF and test myself, please let me know.
You can start doing the ETF setup, if you don't have. It will be very useful for regression testing in the future.
#45 Updated by Alexandru Lungu 15 days ago
#46 Updated by Teodor Gorghe 15 days ago
Teodor Gorghe wrote:
- [MAJOR] functional
Block.rollback/SavepointManager.rollback: Removing thetxLevel > 0 && savepoint == nullearly-out means a block now performs in-memory rollback (dmo.rollback(txLevel), marking records STALE) even when no DB savepoint was ever set. This is required by the deferred model, but it changes rollback semantics for savepoint-less blocks and must be validated against the case where the DB write did happen under a different block's savepoint than the one tracking the record.
I have forgot to mention. I have reverted this change from 11573a and tested again with ETF. From 10 tests which are failing, I have obtained only 9 failed tests.
#47 Updated by Alexandru Lungu 15 days ago
Artur, I committed to testcases/1847 plenty of tests in tests/persistence/flushing_validation/flush_pipeline_regression that explore this task. In trunk, I have 9 failures, while with 11573a I have 10 failures. I attached the test.
#48 Updated by Artur Școlnic 15 days ago
Thank you, Alex.
I will fix the test case, then retest etf, hopefully it will be enough.
#49 Updated by Artur Școlnic 14 days ago
Constantin,
The regression in ETF comes from the second set of changes, but rev 16622-16623 addresses one of the issues discovered in the customer application and Eduard tested these changes separately with etf, they passed. I propose we deliver them now in the existing branch and make a 'b' branch for the second set of changes, I still have to work on those.
#50 Updated by Constantin Asofiei 14 days ago
Artur Școlnic wrote:
Constantin,
The regression in ETF comes from the second set of changes, but rev 16622-16623 addresses one of the issues discovered in the customer application and Eduard tested these changes separately with etf, they passed. I propose we deliver them now in the existing branch and make a 'b' branch for the second set of changes, I still have to work on those.
Please update a and b branches and I'll look at them.
#51 Updated by Artur Școlnic 14 days ago
Done. Now 11573b contains all the changes and I will continue working on it, but 11573a contains only the trigger related changes that are fully tested and safe.
#52 Updated by Constantin Asofiei 14 days ago
Artur Școlnic wrote:
Done. Now 11573b contains all the changes and I will continue working on it, but 11573a contains only the trigger related changes that are fully tested and safe.
Please confirm what was tested with 11573a.
#53 Updated by Artur Școlnic 13 days ago
I tested the multi tenant project, large gui application unit tests and etf, all passed.
#54 Updated by Constantin Asofiei 13 days ago
- Status changed from Review to Merge Pending
Artur Școlnic wrote:
I tested the multi tenant project, large gui application unit tests and etf, all passed.
You can merge now.
#55 Updated by Artur Școlnic 13 days ago
- % Done changed from 100 to 90
- Status changed from Merge Pending to WIP
11573a was merged to trunk/16643 and custom branch rev 16429.
#56 Updated by Constantin Asofiei 8 days ago
Eduard, I think this is the recreate for the regression in 11573b:
do transaction:
for each book: delete book. end.
end.
def var hb as handle.
create buffer hb for table "book".
procedure procCreate.
hb:buffer-create().
run procCreate2. // this doesn't seem to be important
end.
procedure procCreate2.
end.
procedure procUpdate.
run procUpdate2. // this call is important, this is what leaks the savepoint
end.
procedure procUpdate2.
hb::isbn = "abcdef".
hb::book-id = 1234.
end.
procedure raiseError.
def var h as handle.
h = h:next-sibling.
end.
do transaction:
create book.
book.isbn = "zdef".
book.book-id = 9876.
do transaction:
run procCreate.
run raiseError. // this does a rollback
run procUpdate.
run raiseError. // this messes up the 'hb' record
end.
end.
for each book:
message book.book-id book.book-title.
end.
#57 Updated by Artur Școlnic 7 days ago
Yes, this is regressed by 11573b, thank you very much, Constantin.
#58 Updated by Artur Școlnic 7 days ago
- Status changed from WIP to Review
The problem was that rollback condition in SavepointManager.rollback() was too broad, trying to compensate for the fact that rollback can now happen for in memory changes that no longer have db savepoints, it was not necessary as it could lead to unwanted rollbacks as in the test case above.
I reverted the change to rollback condition and tidied up some code based on observation on the task. Retested etf and now it is passing.
Constantin, please review rev 16646.
#59 Updated by Eric Faulhaber 7 days ago
What additional testing does this need, once it is reviewed?
#60 Updated by Artur Școlnic 7 days ago
I can do another 2 projects besides etf. I will also ask for help with chui regression testing. If the review is positive, testing can be done by tomorrow EOD.
#61 Updated by Constantin Asofiei 7 days ago
Ovidiu: please review also.
#62 Updated by Ovidiu Maxiniuc 7 days ago
Actually, I'm in progress of doing it.
#63 Updated by Ovidiu Maxiniuc 7 days ago
Review of 11573b / r16646
I was interested in balancing of the savepoints because of another task and I had a look over 11573b.
The branch is very nicely documented, the javadocs are explaining local code. I like that, it makes the reader's job easier.
SavepointManager.java- at lines 538/540: I would add a small comment here. It looks like
previouscan be inlined. However, this would not be correct because the call tolazilySetSavepoints()may and probably will have as side-effect the change ofchangeBlock.savepoint; - line 761: I think
DmlSavepointcan bestatic;
- at lines 538/540: I would add a small comment here. It looks like
Session.java- lines 1002, 1048-1052:
dmlSavepointis received as parameter and updated, but not used for anything else. Something like the block at 1176-1179 is missing.
- lines 1002, 1048-1052:
#64 Updated by Artur Școlnic 7 days ago
Ovidiu, thank you for catching these, I addressed the issues in rev 16647, please take a look again.
#66 Updated by Artur Școlnic 6 days ago
- Status changed from Review to WIP
There are some regressions in tests Constantin provided in #11625, working on a fix.
#67 Updated by Artur Școlnic 6 days ago
- Status changed from WIP to Review
I fixed the regressions and some issues in trunk, details in #11625. I did a bit of a rework so the changes are better reviewed as a diff from trunk and not rev 16647.
Constantin or Ovidiu, please review rev 16648.
#68 Updated by Constantin Asofiei 6 days ago
Artur, about the "flush before delete" - I think this may be problematic. The change exists only in memory until flushing. But flushing may result in an unique index collision - and delete will fail because this. Please test this,
#69 Updated by Artur Școlnic 6 days ago
You are right, if the flush fails, the delete fails also. I will look for another solution.
#70 Updated by Artur Școlnic 6 days ago
I checked and this does not make much sense in 4gl also
do transaction:
find first Book exclusive-lock.
Book.book-id = 1. // assume another record exists with id 1, this will raise an error and the block below will not execute.
do on error undo, leave:
delete Book.
undo, leave.
end.
end.
It looks like in 4gl and FWD you can't assign a duplicate value in memory then delete it in another transaction anyway.
Still, flushing the changes during a delete maybe is not the best approach, I will check if there are other side effects, maybe triggers.
#71 Updated by Ovidiu Maxiniuc 6 days ago
Artur Școlnic wrote:
I fixed the regressions and some issues in trunk, details in #11625. I did a bit of a rework so the changes are better reviewed as a diff from trunk and not rev 16647.
Constantin or Ovidiu, please review rev 16648.
I see. The rework is quite extensive. I did not see anything that might look incorrect. Just an observation related to newly added stateSet from ChangeSet.java. If I understand it well, the same effect could have be obtained by changing int[] states to Integer[] states, with the following conversion semantics: stateSet[i] = (states[i] != null).
TBO, as I described the problem I encountered, I though the problem was simpler, just to fix the nested blocks counter for lazy savepoints setup. However, the solution looks much more complex.
#72 Updated by Artur Școlnic 5 days ago
I reverted some of the fixes for tests failing in trunk as they are incomplete and out of scope, Ovidiu, please review rev 16649.
#73 Updated by Ovidiu Maxiniuc 5 days ago
- Status changed from Review to Internal Test
Yes, the remaining changes from 11573b are a subset of the previous revision. The removed changes have potential. Will they be added to another branch?
#74 Updated by Artur Școlnic 5 days ago
I don't think so, as Alex said in #11625, only keeping track of the dmo state is not enough, there are RecordNursery and dirty tracking implications I did not consider, as I understand this issue is worked in #6923.
Will start testing with 11573b.
#75 Updated by Constantin Asofiei 1 day ago
- Status changed from Internal Test to Merge Pending
11573b can be merged now.
#76 Updated by Artur Școlnic 1 day ago
- Status changed from Merge Pending to Test
11573b was merged to trunk/16658 and 10614_main/16432.