Bug #9262
batch assign updates the dirty database on each assignment
0%
History
#2 Updated by Constantin Asofiei almost 2 years ago
I found some cases where DirtyShareContext.insert is being called on each assignment in a batch assign, and not just at the end of the batch. This is accessing the dirty database via an insert/update, and it adds up.
An example which shows this is; in FWD, it takes ~1.5 seconds, while in OE ~350ms. Not all the time is spent in dirty database (I think ~200ms in FWD).
for each book transaction:
delete book.
end.
def var i as int.
def var m1 as int64.
def var m2 as int64.
def var ch as char.
def var d as date.
d = today.
m1 = mtime.
do i = 1 to 1000 transaction:
ch = string(i).
do transaction:
create book.
assign book.book-id = i
book.isbn = ch
book.book-title = ch
book.author-id = i
book.publisher = ch
book.pub-date = d.
release book.
end.
end.
m2 = mtime.
message m2 - m1 "ms".
On each assignment in the batch-assign, the H2 dirty-database will be 'hit':
DefaultDirtyShareManager.trackChange(String, Record, List<String>, String[], boolean, IndexState) line: 1724 DefaultDirtyShareManager.insert(String, Record, IndexState, boolean) line: 418 DirtyShareContext.insert(RecordBuffer, Record, boolean) line: 353 RecordBuffer$Handler.invoke(Object, Method, Object[]) line: 12544 $__Proxy5.setBookId(NumberType) line: not available DirtyInsert.lambda$6() line: 75 13892413.run() line: not available RecordBuffer.batch(Runnable) line: 3322 DirtyInsert.lambda$5() line: 73 1569400373.body() line: not available Block.body() line: 636 BlockManager.processBody(BlockManager$WorkArea, Block, OnPhrase[], OnPhrase[]) line: 9291 BlockManager.doBlockWorker(BlockManager$TransactionType, String[], String, OnPhrase[], Block) line: 10537 BlockManager.doBlock(BlockManager$TransactionType, String, Block) line: 1535 DirtyInsert.lambda$4() line: 70 492258452.body() line: not available Block.body() line: 636 BlockManager.processBody(BlockManager$WorkArea, Block, OnPhrase[], OnPhrase[]) line: 9291 BlockManager.coreLoop(BlockType, boolean, BlockManager$TransactionType, String[], String, ToClause, LogicalOp, OnPhrase[], OnPhrase[], Block) line: 10999 BlockManager.doLoopWorker(BlockManager$TransactionType, String[], String, ToClause, LogicalOp, OnPhrase[], Block) line: 10826 BlockManager.doTo(BlockManager$TransactionType, String, ToClause, Block) line: 1929 DirtyInsert.lambda$0() line: 66
In the #9214 scenario, DirtyShareContext.insert is executed ~0.5 million times, for each individual assignment in the batch assign.
I think we should delay this until batch ends, but a question here is: what happens if we are in the batch assign, but a function is called? I don't recall if we 'pause' this batch assign so the function is not executed in the 'batch assign' context.
#3 Updated by Alexandru Lungu almost 2 years ago
Can you do a similar test with 7156c? 7156c has a reword of RecordNursery and how the dirty changes are registered. More specific, in 7156c the setter is not updating the dirty database - it will only push the changes to the RecordNursery and cache them. The performance drawback you are seeing is due to an aggressive interaction with the dirty database that comes from 6667i in order to make the changes visible to cross-session ASAP. Disabling cross-session should avoid doing such registration.
I think I will need to fast-track the RecordNursery rework from 7156c (former 6667g changes) to trunk to avoid such issues.
#4 Updated by Constantin Asofiei almost 2 years ago
- Status changed from New to Hold
Yes, with 7156c DirtyShareContext.insert is being called on batch end, so assuming the changes in 7156c will reach trunk, I'm placing this on hold. But, with trunk it is also being called on buffer.create() - this no longer happens:
DirtyShareContext.insert(RecordBuffer, Record, boolean) line: 327 RecordNursery.indexUpdated(RecordBuffer, BitSet, boolean, BaseRecord) line: 158 RecordBuffer.create() line: 10248 $__Proxy5(BufferImpl).create() line: 1665
Is this intended?
#5 Updated by Alexandru Lungu almost 2 years ago
Yes, in 7156c the RecordNursery will hold onto all inserts/updates till the first query will occur. This is how trunk pre-6667i was working anyway (but only for CREATES as UPDATES were not supported by RecordNursery). Post-6667i, everything is sent to the dirty database super-eagerly.
Note however that 7156c was not tested against cross-session cases. It was designed to support intra-session as best as possible. Leveraging the cross-session with 7156c implementation is WIP. Most probably the directory.xml settings will decide if super-eager dirty database flushing should happen or not. In other words, if cross-session will be enabled, everything will be sent to the dirty database ASAP and RecordNursery will be a no-op. If cross-session will be disabled, RecordNursery will cache all operations as much as possible.
Your case in #9262-2 will still be slow if cross-session is enabled. But if it will be disabled and 7156c chanegs will reach trunk, the problem will be fixed.