pinpoint-apm/pinpoint · critical · IllegalStateException

Corrupted schema change logs. Duplicate change set :

Error message

Corrupted schema change logs. Duplicate change set : 

What it means

Thrown by SchemaChangeLogServiceImpl.getSchemaChangeLogs when the same change set id appears in more than one change log record. The schemaChangeLogIds set add() returning false detects the duplicate id. Each change set may be executed and logged only once, so duplicate ids mean the log history is corrupted.

Source

Thrown at hbase/hbase-schema/src/main/java/com/navercorp/pinpoint/hbase/schema/service/SchemaChangeLogServiceImpl.java:124

                .build();
        schemaChangeLogDao.insertChangeLog(namespace, schemaChangeLog);
        return schemaChangeLog;
    }

    @Override
    public List<SchemaChangeLog> getSchemaChangeLogs(String namespace) {
        List<SchemaChangeLog> schemaChangeLogs = schemaChangeLogDao.getChangeLogs(namespace);
        Map<Integer, SchemaChangeLog> orderedSchemaChangeLogs = new TreeMap<>();
        Set<String> schemaChangeLogIds = new HashSet<>();
        for (SchemaChangeLog schemaChangeLog : schemaChangeLogs) {
            Integer execOrder = schemaChangeLog.getExecOrder();
            String id = schemaChangeLog.getId();
            SchemaChangeLog previousLog = orderedSchemaChangeLogs.put(execOrder, schemaChangeLog);
            if (previousLog != null) {
                throw new IllegalStateException("Corrupted schema change logs. Duplicate order for change set : " + id);
            }
            if (!schemaChangeLogIds.add(schemaChangeLog.getId())) {
                throw new IllegalStateException("Corrupted schema change logs. Duplicate change set : " + id);
            }
        }
        return new ArrayList<>(orderedSchemaChangeLogs.values());
    }

    @Override
    public SchemaChangeLog getSchemaChangeLog(String namespace, String id) {
        Assert.hasLength(id, "namespace must not be empty");
        return schemaChangeLogDao.getChangeLog(namespace, id);
    }

}

View on GitHub (pinned to 744c3d3075)

Solutions

  1. Delete or merge the duplicate log rows so each change set id appears exactly once.
  2. Restore the change log table from a backup created before the duplicate rows appeared.
  3. Add/keep a uniqueness guarantee (id as row key) so writes of the same change set are idempotent, and serialize schema updates with a lock.
  4. If the history is unrecoverable, re-initialize the namespace schema from scratch.

Example fix

// before: two rows with id='create-agent-table'
// after: keep the first executed record, remove the extra row
delete 'schema_change_log', 'create-agent-table-dup'
Defensive patterns

Strategy: try-catch

Validate before calling

Map<String, Long> byId = logs.stream()
    .collect(Collectors.groupingBy(SchemaChangeLog::getId, Collectors.counting()));
boolean corrupt = byId.values().stream().anyMatch(c -> c > 1);

Try / catch

try { logs = schemaChangeLogService.getSchemaChangeLogs(namespace); } catch (IllegalStateException e) {
    if (e.getMessage().contains("Duplicate change set")) { alertOps("Corrupt change log: duplicate changeSet id"); }
}

Prevention

When it happens

Trigger: Reading change logs where two rows share an identical id (changeSetId) value, regardless of their execOrder values.

Common situations: Retried schema updates that double-wrote the log row (missing uniqueness on id); manual inserts into the log table; restoring a backup over a live table creating overlapping rows; concurrent writers racing to log the same change set.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07). Data as JSON: /api/errors/d6ba136945344e9b. Report an issue: GitHub.