{"record":{"id":"d6ba136945344e9b","repo":"pinpoint-apm/pinpoint","slug":"corrupted-schema-change-logs-duplicate-change-set","errorCode":null,"errorMessage":"Corrupted schema change logs. Duplicate change set : ","messagePattern":"Corrupted schema change logs\\. Duplicate change set : ","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"critical","filePath":"hbase/hbase-schema/src/main/java/com/navercorp/pinpoint/hbase/schema/service/SchemaChangeLogServiceImpl.java","lineNumber":124,"sourceCode":"                .build();\n        schemaChangeLogDao.insertChangeLog(namespace, schemaChangeLog);\n        return schemaChangeLog;\n    }\n\n    @Override\n    public List<SchemaChangeLog> getSchemaChangeLogs(String namespace) {\n        List<SchemaChangeLog> schemaChangeLogs = schemaChangeLogDao.getChangeLogs(namespace);\n        Map<Integer, SchemaChangeLog> orderedSchemaChangeLogs = new TreeMap<>();\n        Set<String> schemaChangeLogIds = new HashSet<>();\n        for (SchemaChangeLog schemaChangeLog : schemaChangeLogs) {\n            Integer execOrder = schemaChangeLog.getExecOrder();\n            String id = schemaChangeLog.getId();\n            SchemaChangeLog previousLog = orderedSchemaChangeLogs.put(execOrder, schemaChangeLog);\n            if (previousLog != null) {\n                throw new IllegalStateException(\"Corrupted schema change logs. Duplicate order for change set : \" + id);\n            }\n            if (!schemaChangeLogIds.add(schemaChangeLog.getId())) {\n                throw new IllegalStateException(\"Corrupted schema change logs. Duplicate change set : \" + id);\n            }\n        }\n        return new ArrayList<>(orderedSchemaChangeLogs.values());\n    }\n\n    @Override\n    public SchemaChangeLog getSchemaChangeLog(String namespace, String id) {\n        Assert.hasLength(id, \"namespace must not be empty\");\n        return schemaChangeLogDao.getChangeLog(namespace, id);\n    }\n\n}\n","sourceCodeStart":106,"sourceCodeEnd":137,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/hbase/hbase-schema/src/main/java/com/navercorp/pinpoint/hbase/schema/service/SchemaChangeLogServiceImpl.java#L106-L137","documentation":"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.","triggerScenarios":"Reading change logs where two rows share an identical id (changeSetId) value, regardless of their execOrder values.","commonSituations":"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.","solutions":["Delete or merge the duplicate log rows so each change set id appears exactly once.","Restore the change log table from a backup created before the duplicate rows appeared.","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.","If the history is unrecoverable, re-initialize the namespace schema from scratch."],"exampleFix":"// before: two rows with id='create-agent-table'\n// after: keep the first executed record, remove the extra row\ndelete 'schema_change_log', 'create-agent-table-dup'","handlingStrategy":"try-catch","validationCode":"Map<String, Long> byId = logs.stream()\n    .collect(Collectors.groupingBy(SchemaChangeLog::getId, Collectors.counting()));\nboolean corrupt = byId.values().stream().anyMatch(c -> c > 1);","typeGuard":null,"tryCatchPattern":"try { logs = schemaChangeLogService.getSchemaChangeLogs(namespace); } catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"Duplicate change set\")) { alertOps(\"Corrupt change log: duplicate changeSet id\"); }\n}","preventionTips":["Use changeSet id as the row key so duplicate writes overwrite instead of appending","Block manual edits of the log table","Guard retry logic so a completed change set is never logged twice"],"tags":["hbase","data-corruption","duplicate-key"],"backgroundTag":"internal-invariant-violation","analyzedSha":"744c3d3075e595656abb1ae331ad2c0e4c9eb996","analyzedAt":"2026-09-07T18:48:45.289Z","contentChangedAt":"2026-09-07T18:48:45.289Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}