[CONJ-1017] Calendar possible race condition, cause wrong timestamp setting Created: 2022-10-05 Updated: 2022-11-07 Resolved: 2022-10-10 |
|
| Status: | Closed |
| Project: | MariaDB Connector/J |
| Component/s: | Other |
| Affects Version/s: | 3.0.8 |
| Fix Version/s: | 3.0.9 |
| Type: | Bug | Priority: | Major |
| Reporter: | Romain Moreau | Assignee: | Diego Dupin |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
For instance, given the following data:
If I insert these rows with 2 threads in parallel (one thread bulk inserting the 5 first rows and another thread bulk inserting the 5 last rows), when I select the inserted rows, the values are randomly scrambled, for instance:
If I insert these rows in bulk without concurrence, there's no issue. Demo : https://github.com/romainmoreau/mariadb-demo |
| Comments |
| Comment by Marko Mäkelä [ 2022-10-05 ] | ||||||||||||||||
|
Which storage engine are you using? If it is InnoDB, you should be aware of | ||||||||||||||||
| Comment by Romain Moreau [ 2022-10-05 ] | ||||||||||||||||
|
Yes my my.ini is pretty much vanilla:
This issue also happens when the table is not empty (I updated the demo). | ||||||||||||||||
| Comment by Marko Mäkelä [ 2022-10-06 ] | ||||||||||||||||
|
InnoDB does row-level locking. It is expected that when multiple connections are inserting data concurrently, the writes can be interleaved. If you want to prevent that, you could escalate to table-level locking by issuing a LOCK TABLE statement. You might also define and use a logical PRIMARY KEY in your database schema design. | ||||||||||||||||
| Comment by Romain Moreau [ 2022-10-06 ] | ||||||||||||||||
|
If this behavior is expected on a server side, then probably this issue should be addressed by the Java connector project. | ||||||||||||||||
| Comment by Diego Dupin [ 2022-10-07 ] | ||||||||||||||||
|
In your example, the behavior is expected :
Will result in hibernate internally executing 5 * JDBC prepareStatement.execute(). There is some solutions hibernate side, like for example adding batch_size :
In that case, hibernate will not run some PrepareStatement.execute, but will execute those in batch (preparestatement.addBatch() x times, then preparestatement.executeBatch) | ||||||||||||||||
| Comment by Romain Moreau [ 2022-10-07 ] | ||||||||||||||||
|
If you debug the demo, you'll see it's not the way you describe because you seem to have missed this line which sets the bach size only for the current hibernate Session instead of setting it globally like you were suggesting:
In fact, this part is not executed in the current settings of the demo (4 objects by thread) but I left it to support lists which have a size higher than BATCH_SIZE (50):
I added the following to show what commands the MariaDB connector client was sending:
And aside from transactions related commands, only two commands are issued by each thread : one prepare (PreparePacket) and one bulk insert (BulkExecutePacket) with no mixed up data when I inspect the batchParameterList field. | ||||||||||||||||
| Comment by Diego Dupin [ 2022-10-09 ] | ||||||||||||||||
|
sorry, i've made bad assumption. This is indeed an issue connector side : calendar use in prepareStatement.setTimestamp(int parameterIndex, Timestamp x, Calendar cal) is not used in synchronized block, resulting in this issue when use in parallel problem is only in 3.0 version | ||||||||||||||||
| Comment by Diego Dupin [ 2022-10-10 ] | ||||||||||||||||
|
corrected in 3.0.9-SNAPSHOT version with commit https://github.com/mariadb-corporation/mariadb-connector-j/commit/22388e698edb045ea9e8c560195caa9bf4bb6da3 available using snapshot repository :
| ||||||||||||||||
| Comment by Romain Moreau [ 2022-10-10 ] | ||||||||||||||||
|
It works great with the demo and also with a real world application, thanks! |