{"record":{"id":"a8d1abdd22ca2289","repo":"alibaba/canal","slug":"rowkey-a8d1ab","errorCode":null,"errorMessage":"没有更新复合主键的RowKey","messagePattern":"没有更新复合主键的RowKey","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/service/HbaseSyncService.java","lineNumber":332,"sourceCode":"        String[] rowKeyColumns = hbaseMapping.getRowKey().trim().split(\",\");\n\n        int index = 0;\n        int i = 1;\n        boolean complete = false;\n        Set<byte[]> rowKeys = new HashSet<>();\n        List<HRow> rows = new ArrayList<>();\n        for (Map<String, Object> r : data) {\n            // 拼接老的rowKey\n            List<String> updateSubRowKey = new ArrayList<>();\n            for (String rowKeyColumnName : rowKeyColumns) {\n                for (String updateColumn : old.get(index).keySet()) {\n                    if (rowKeyColumnName.equalsIgnoreCase(updateColumn)) {\n                        updateSubRowKey.add(rowKeyColumnName);\n                    }\n                }\n            }\n            if (updateSubRowKey.isEmpty()) {\n                throw new RuntimeException(\"没有更新复合主键的RowKey\");\n            }\n            StringBuilder oldRowKey = new StringBuilder();\n            StringBuilder newRowKey = new StringBuilder();\n            for (String rowKeyColumnName : rowKeyColumns) {\n                newRowKey.append(r.get(rowKeyColumnName).toString()).append(\"|\");\n                if (!updateSubRowKey.contains(rowKeyColumnName)) {\n                    // 从data取\n                    oldRowKey.append(r.get(rowKeyColumnName).toString()).append(\"|\");\n                } else {\n                    // 从old取\n                    oldRowKey.append(old.get(index).get(rowKeyColumnName).toString()).append(\"|\");\n                }\n            }\n            int len = newRowKey.length();\n            newRowKey.delete(len - 1, len);\n            len = oldRowKey.length();\n            oldRowKey.delete(len - 1, len);\n            byte[] newRowKeyBytes = Bytes.toBytes(newRowKey.toString());","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/service/HbaseSyncService.java#L314-L350","documentation":"HbaseSyncService.deleteAndInsert() handles UPDATEs that modify a composite rowKey column. It first checks whether ANY composite rowKey column appears in the updated ('old') columns; if none of the rowKey columns were updated, the method cannot have been entered correctly and throws '没有更新复合主键的RowKey' (no composite rowKey column was updated).","triggerScenarios":"deleteAndInsert is invoked (because an update path detected a composite-key column change) but the intersection of the updated column set and the rowKey column list is empty — logically the row did not actually change a key column, yet the delete-insert branch ran.","commonSituations":"Composite rowKey column names in the mapping do not case-match the column names in the DML old/data maps; a logic path mismatch; rowKey column list has a typo so equalsIgnoreCase never matches.","solutions":["Verify the composite rowKey column names in the mapping exactly match (case-insensitively) the column names arriving in the DML.","Remove stale/renamed columns from the rowKey list.","Confirm the update genuinely changed a composite-key column; if not, the regular update() path should handle it.","Re-test the mapping with a known update on one key column."],"exampleFix":"# before (rowKey col name typo)\nhbaseMapping:\n  rowKey: user_id,ts\n# DML columns are 'userId','ts'\n\n# after (match actual column names)\nhbaseMapping:\n  rowKey: userId,ts","handlingStrategy":"validation","validationCode":"// Verify composite rowKey column names match actual DML column names\nString[] rowKeyCols = hbaseMapping.getRowKey().split(\",\");\nSet<String> dmlCols = dml.getData().get(0).keySet();\nfor (String c : rowKeyCols) {\n    boolean found = dmlCols.stream().anyMatch(d -> d.equalsIgnoreCase(c.trim()));\n    if (!found) throw new IllegalStateException(\"rowKey col '\" + c + \"' not present in DML columns \" + dmlCols);\n}","typeGuard":null,"tryCatchPattern":"try {\n    // invoked internally from update() on composite-key change\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"复合主键\")) {\n        logger.error(\"Composite rowKey column names do not match DML columns; fix mapping\");\n    }\n    throw e;\n}","preventionTips":["Make composite rowKey column names match DML column names (case-insensitive).","Remove stale columns from the rowKey list after schema renames.","Test composite-key updates against the mapping in a staging environment."],"tags":["hbase-adapter","sync","rowkey","composite-key","update"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}