Hibernate оптимистическая блокировка различного поведения между Postgres и MariaDb - PullRequest
0 голосов
/ 19 октября 2018

Я только что обнаружил, что мое приложение ведет себя по-разному, когда я использую оптимистическую блокировку с базой данных Postgresql или MariaDB, и мне интересно, может ли кто-нибудь объяснить, что происходит, и как я могу заставить приложение работать таким же образом с MariaDB?Я использую Postgresl 10.5 и MariaDB 10.3.10 с движком InnoDB и настройками по умолчанию.Я использую Spring Framework версии 5.1.0 и Hibernate 5.3.6.

Так что мой код выглядит так:

@Entity
@Getter
@Setter
@NoArgsConstructor
public class Bla {

    @Id
    @GeneratedValue
    private long id;

    @Version
    private long version;

    private int counter;
}

У меня также есть хранилище для этой сущности и следующий метод обслуживания:

@Transactional
public int increment(long id) {
    Bla bla = blaRepo.getOne(id);
    bla.setCounter(bla.getCounter() + 1);
    return bla.getCounter();
}

Если я вызову этот метод в нескольких потоках, я ожидаю, что обновление будет успешным только для одного из них, если они коснутся сущности с одинаковой версией.В качестве примера: если я запускаю 50 потоков с Postgres db за один прогон, я получаю 3 вызова, которые завершаются успешно, и возвращают значения 1, 2, 3, а остальные 47 завершаются ошибкой с ObjectOptimisticLockingFailureException, который является ожидаемым поведением - это то, как я хотел быприложение вести себя.

Однако, если я переключусь на MariaDB, этого не произойдет.Все 50 этих потоков успешно завершены, и я получаю одно и то же значение ответа в нескольких потоках, как если бы не было оптимистической блокировки.Например, теперь первые 5 потоков вернули 1, затем 20 из них вернули 2, а остальные 3 или 4.

Почему это происходит?Это не имеет никакого смысла - для обеих баз данных сгенерированный запрос имеет вид

update bla set counter=?, version=? where id=? and version=?

Но в Postgresql произойдет сбой правильно, а с MariaDB неожиданно получится.

Ответы [ 2 ]

0 голосов
/ 19 октября 2018

Я нашел решение этой проблемы.

Кажется, у меня установлено это свойство в application.properties:

spring.jpa.properties.hibernate.jdbc.batch_size = 50

Когда я использую Postgresql, я получаю следующую трассировку отладки с двумя потоками:

13223 [pool-2-thread-2] DEBUG org.hibernate.SQL - update bla set counter=?, version=? where id=? and version=?
13223 [pool-2-thread-1] DEBUG org.hibernate.SQL - update bla set counter=?, version=? where id=? and version=? 
13226 [pool-1-thread-1] DEBUG org.hibernate.engine.jdbc.batch.internal.BatchingBatch - Executing batch size: 1
13226 [pool-1-thread-2] DEBUG org.hibernate.engine.jdbc.batch.internal.BatchingBatch - Executing batch size: 1
13230 [pool-1-thread-1] ERROR org.hibernate.engine.jdbc.batch.internal.BatchingBatch - HHH000315: Exception executing batch [org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1], SQL: update bla set counter=?, version=? where id=? and version=?

и затем с MariaDB с таким же размером пакета 50:

21978 [pool-2-thread-2] DEBUG org.hibernate.SQL - update bla set counter=?, version=? where id=? and version=?
21978 [pool-2-thread-1] DEBUG org.hibernate.SQL - update bla set counter=?, version=? where id=? and version=? 
21979 [pool-2-thread-2] DEBUG org.hibernate.engine.jdbc.batch.internal.BatchingBatch - Executing batch size: 1
21979 [pool-2-thread-1] DEBUG org.hibernate.engine.jdbc.batch.internal.BatchingBatch - Executing batch size: 1
21980 [pool-2-thread-2] DEBUG org.hibernate.jdbc.Expectations - Success of batch update unknown: 0
21980 [pool-2-thread-1] DEBUG org.hibernate.jdbc.Expectations - Success of batch update unknown: 0

, а затем с MariaDB с размером пакета 1:

12994 [pool-2-thread-2] DEBUG org.hibernate.SQL - update bla set counter=?, version=? where id=? and version=?
12994 [pool-2-thread-1] DEBUG org.hibernate.SQL - update bla set counter=?, version=? where id=? and version=?
12997 [pool-2-thread-1] DEBUG org.hibernate.cache.internal.TimestampsCacheEnabledImpl - Invalidating space [bla], timestamp: 6307671153053696
12998 [pool-2-thread-2] DEBUG org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl - JDBC transaction marked for rollback-only (exception provided for stack trace)

И теперь действительно приложение выдает ожидаемое исключение ObjectOptimisticLockingFailureException

Но, к сожалению, это означает, что использование MariaDb с оптимистической блокировкой для объектов и любого размера пакета больше 1 несовместимо.

0 голосов
/ 19 октября 2018

Единственный способ, которым это может случиться, - это ошибка MariaDB, потому что как только Tx изменяет запись, он блокирует ее до тех пор, пока не выполнит фиксацию или откат.Другой Tx будет блокировать UPDATE из-за блокировки, но условие должно быть переоценено после снятия блокировки.

Попробуйте переключиться на READ_COMMITTED и посмотрите, устраняет ли это проблему.Это может быть аномалия REPEATABLE_READ.

...