alibaba/canal · error · RuntimeException

没有更新复合主键的RowKey

Error message

没有更新复合主键的RowKey

What it means

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).

Source

Thrown at client-adapter/hbase/src/main/java/com/alibaba/otter/canal/client/adapter/hbase/service/HbaseSyncService.java:332

        String[] rowKeyColumns = hbaseMapping.getRowKey().trim().split(",");

        int index = 0;
        int i = 1;
        boolean complete = false;
        Set<byte[]> rowKeys = new HashSet<>();
        List<HRow> rows = new ArrayList<>();
        for (Map<String, Object> r : data) {
            // 拼接老的rowKey
            List<String> updateSubRowKey = new ArrayList<>();
            for (String rowKeyColumnName : rowKeyColumns) {
                for (String updateColumn : old.get(index).keySet()) {
                    if (rowKeyColumnName.equalsIgnoreCase(updateColumn)) {
                        updateSubRowKey.add(rowKeyColumnName);
                    }
                }
            }
            if (updateSubRowKey.isEmpty()) {
                throw new RuntimeException("没有更新复合主键的RowKey");
            }
            StringBuilder oldRowKey = new StringBuilder();
            StringBuilder newRowKey = new StringBuilder();
            for (String rowKeyColumnName : rowKeyColumns) {
                newRowKey.append(r.get(rowKeyColumnName).toString()).append("|");
                if (!updateSubRowKey.contains(rowKeyColumnName)) {
                    // 从data取
                    oldRowKey.append(r.get(rowKeyColumnName).toString()).append("|");
                } else {
                    // 从old取
                    oldRowKey.append(old.get(index).get(rowKeyColumnName).toString()).append("|");
                }
            }
            int len = newRowKey.length();
            newRowKey.delete(len - 1, len);
            len = oldRowKey.length();
            oldRowKey.delete(len - 1, len);
            byte[] newRowKeyBytes = Bytes.toBytes(newRowKey.toString());

View on GitHub (pinned to 87be50e876)

Solutions

  1. Verify the composite rowKey column names in the mapping exactly match (case-insensitively) the column names arriving in the DML.
  2. Remove stale/renamed columns from the rowKey list.
  3. Confirm the update genuinely changed a composite-key column; if not, the regular update() path should handle it.
  4. Re-test the mapping with a known update on one key column.

Example fix

# before (rowKey col name typo)
hbaseMapping:
  rowKey: user_id,ts
# DML columns are 'userId','ts'

# after (match actual column names)
hbaseMapping:
  rowKey: userId,ts
Defensive patterns

Strategy: validation

Validate before calling

// Verify composite rowKey column names match actual DML column names
String[] rowKeyCols = hbaseMapping.getRowKey().split(",");
Set<String> dmlCols = dml.getData().get(0).keySet();
for (String c : rowKeyCols) {
    boolean found = dmlCols.stream().anyMatch(d -> d.equalsIgnoreCase(c.trim()));
    if (!found) throw new IllegalStateException("rowKey col '" + c + "' not present in DML columns " + dmlCols);
}

Try / catch

try {
    // invoked internally from update() on composite-key change
} catch (RuntimeException e) {
    if (e.getMessage() != null && e.getMessage().contains("复合主键")) {
        logger.error("Composite rowKey column names do not match DML columns; fix mapping");
    }
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14). Data as JSON: /api/errors/a8d1abdd22ca2289. Report an issue: GitHub.