"use strict";
/*
 * server component for the TimeLimit App
 * Copyright (C) 2019 - 2022 Jonas Lochmann
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.SerializationFeatureCheckException = void 0;
exports.shouldRetryWithException = shouldRetryWithException;
exports.assertSerializeableTransactionsAreWorking = assertSerializeableTransactionsAreWorking;
const Sequelize = require("sequelize");
const config_1 = require("../config");
class SerializationFeatureCheckException extends Error {
}
exports.SerializationFeatureCheckException = SerializationFeatureCheckException;
function shouldRetryWithException(database, e) {
    if (e instanceof Sequelize.TimeoutError)
        return true;
    if (!(e instanceof Sequelize.DatabaseError))
        return false;
    const parent = e.parent;
    if (database.dialect === 'sqlite') {
        if (parent.message.startsWith('SQLITE_BUSY:'))
            return true;
    }
    else if (database.dialect === 'postgres') {
        // 40001 = serialization_failure
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        if (parent.code === '40001')
            return true;
        // 40P01 = deadlock detected
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        if (parent.code === '40P01')
            return true;
    }
    else if (database.dialect === 'mariadb') {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const errno = parent.errno;
        // ER_LOCK_DEADLOCK
        // Deadlock found when trying to get lock; try restarting transaction
        if (errno === 1213)
            return true;
    }
    return false;
}
async function assertSerializeableTransactionsAreWorking(database) {
    // clean up just for the case
    await database.config.destroy({
        where: {
            id: {
                [Sequelize.Op.in]: [config_1.configItemIds.selfTestData, config_1.configItemIds.secondSelfTestData]
            }
        }
    });
    // insert specific data
    await database.config.bulkCreate([
        {
            id: config_1.configItemIds.selfTestData,
            value: '123'
        },
        {
            id: config_1.configItemIds.secondSelfTestData,
            value: '456'
        }
    ]);
    try {
        // use two parallel transactions
        await database.transaction(async (transactionOne) => {
            await database.transaction(async (transactionTwo) => {
                await database.config.findAll({ transaction: transactionOne });
                await database.config.findAll({ transaction: transactionTwo });
                await Promise.all([
                    (async () => {
                        await database.config.update({ value: 'c' }, { where: { id: config_1.configItemIds.selfTestData }, transaction: transactionOne });
                    })(),
                    (async () => {
                        await database.config.update({ value: 'd' }, { where: { id: config_1.configItemIds.secondSelfTestData }, transaction: transactionTwo });
                    })()
                ]);
            }, { disableRetry: true });
        }, { disableRetry: true });
        throw new SerializationFeatureCheckException();
    }
    catch (ex) {
        if (!shouldRetryWithException(database, ex)) {
            throw new SerializationFeatureCheckException();
        }
    }
    // finish clean up
    await database.config.destroy({
        where: {
            id: {
                [Sequelize.Op.in]: [config_1.configItemIds.selfTestData, config_1.configItemIds.secondSelfTestData]
            }
        }
    });
}
//# sourceMappingURL=serialized.js.map