Skip to content

InsertRowLock requires the database to implement primary keys #4164

@jaccozilla

Description

@jaccozilla

I'm looking at adding support for locking in clickhouse flyway/flyway-community-db-support#48. InsertRowLock is close to working, but because clickhouse doesn't support traditional primary keys I had to make some modifications to it.

I'm allowing multiple locks to be written, but consistently selecting a winner by ordering on timestamp + version. This needs the insertLockingRow to also query the current owner.

I would like to refactor InsertRowLock a little and pull out an AbstractInsertRowLock allowing for subclasses to implement insertLockingRow.

This is what i'm currently using for clickhouse's `insertRowLock

private boolean insertLockingRow(String insertStatementTemplate, String currentLockOwnerStatementTemplate, String booleanTrue, String checksumValue) throws InterruptedException {

        String currentLockOwner = getCurrentLockOwner(currentLockOwnerStatementTemplate);
        if (tableLockString.equals(currentLockOwner)) {
            return true;
        }
        if (currentLockOwner != null) {
            // owned by another migration
            LOG.info("[{}] Unable to lock, already owned by {}",  tableLockString, currentLockOwner);
            return false;
        }

        String insertStatement = String.format(insertStatementTemplate.replace("?", "%s"),
                                               -100,
                                               "'" + tableLockString + "'",
                                               "'" + FLYWAY_LOCK_STRING + "'",
                                               "''",
                                               "''",
                                               checksumValue,
                                               "''",
                                               0,
                                               booleanTrue
                                              );

        // Insert the locking row - the primary key-ness of 'installed_rank' will prevent us having two
        Results results = jdbcTemplate.executeStatement(insertStatement);

        if (results.getException() != null) {
            return false;
        }

        if (currentLockOwnerStatementTemplate == null) {
            return true;
        }

        currentLockOwner = getCurrentLockOwner(currentLockOwnerStatementTemplate);
        if (tableLockString.equals(currentLockOwner)) {
            // double check we are still the current lock owner, in case we raced with another
            // insert and need version ordering to select the current owner
            currentLockOwner = getCurrentLockOwner(currentLockOwnerStatementTemplate);
            if (tableLockString.equals(currentLockOwner)) {
                // this row lock is the owner
                return true;
            }
        }

        // not the owner
        LOG.info("[{}] Attempted to lock, but already owned by {}",  tableLockString, currentLockOwner);
        return false;
    }

    private String getCurrentLockOwner(String currentLockOwnerStatementTemplate) {
        if (currentLockOwnerStatementTemplate == null) {
            return null;
        }
        String currentLockOwnerStatement = String.format(currentLockOwnerStatementTemplate.replace("?", "%s"),
                "'" + FLYWAY_LOCK_STRING + "'"
        );

        Results results = jdbcTemplate.executeStatement(currentLockOwnerStatement);

        for (Result result : results.getResults()) {
            List<List<String>> data = result.data();
            if (data == null || data.isEmpty()) {
                continue;
            }
            int columnIndex = result.columns().indexOf("version");
            if (columnIndex == -1) {
                throw new IllegalStateException("missing 'version' column in currentLockOwnerStatementTemplate: " + currentLockOwnerStatementTemplate);
            }

            return data.getFirst().get(columnIndex);
        }
        return null;
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions