prestodb/presto · error
FUNCTION_IMPLEMENTATION_ERROR
FUNCTION_IMPLEMENTATION_ERROR
Error message
loadAll called with a non-homogeneous collection of cache keys
What it means
ColumnCardinalityCache.loadAll (a LoadingCache CacheLoader) assumes, as an implementation simplification, that every CacheKey in a batch shares the same schema/table/family/qualifier so a single metrics-table scan can serve the whole batch. When any key differs in one of those fields it throws FUNCTION_IMPLEMENTATION_ERROR - an internal invariant violation, not user input error.
Source
Thrown at presto-accumulo/src/main/java/com/facebook/presto/accumulo/index/ColumnCardinalityCache.java:376
}
@Override
public Map<CacheKey, Long> loadAll(Iterable<? extends CacheKey> keys)
throws Exception
{
int size = Iterables.size(keys);
if (size == 0) {
return ImmutableMap.of();
}
LOG.debug("Loading %s exact ranges from Accumulo", size);
// In order to simplify the implementation, we are making a (safe) assumption
// that the CacheKeys will all contain the same combination of schema/table/family/qualifier
// This is asserted with the below implementation error just to make sure
CacheKey anyKey = stream(keys).findAny().get();
if (stream(keys).anyMatch(k -> !k.getSchema().equals(anyKey.getSchema()) || !k.getTable().equals(anyKey.getTable()) || !k.getFamily().equals(anyKey.getFamily()) || !k.getQualifier().equals(anyKey.getQualifier()))) {
throw new PrestoException(FUNCTION_IMPLEMENTATION_ERROR, "loadAll called with a non-homogeneous collection of cache keys");
}
Map<Range, CacheKey> rangeToKey = stream(keys).collect(Collectors.toMap(CacheKey::getRange, Function.identity()));
LOG.debug("rangeToKey size is %s", rangeToKey.size());
// Get metrics table name and the column family for the scanner
String metricsTable = getMetricsTableName(anyKey.getSchema(), anyKey.getTable());
Text columnFamily = new Text(getIndexColumnFamily(anyKey.getFamily().getBytes(UTF_8), anyKey.getQualifier().getBytes(UTF_8)).array());
BatchScanner scanner = connector.createBatchScanner(metricsTable, anyKey.getAuths(), 10);
try {
scanner.setRanges(stream(keys).map(CacheKey::getRange).collect(Collectors.toList()));
scanner.fetchColumn(columnFamily, CARDINALITY_CQ_AS_TEXT);
// Create a new map to hold our cardinalities for each range, returning a default of
// Zero for each non-existent Key
Map<CacheKey, Long> rangeValues = new HashMap<>();
stream(keys).forEach(key -> rangeValues.put(key, 0L));View on GitHub (pinned to 55bb57d202)
Solutions
- Do not call ColumnCardinalityCache.loadAll directly; go through getCardinalities, which batches keys correctly.
- If you modified the cache or its callers, partition keys by (schema, table, family, qualifier) and issue one loadAll call per group.
- If you hit this in a stock connector, file a bug with the full stack trace - it indicates a connector defect.
- In tests, build CacheKeys that all share the same schema/table/family/qualifier.
Example fix
// before
cache.loadAll(keys); // keys span multiple tables/columns
// after
Map<List<String>, List<CacheKey>> grouped = keys.stream()
.collect(Collectors.groupingBy(k -> Arrays.asList(k.getSchema(), k.getTable(), k.getFamily(), k.getQualifier())));
grouped.values().forEach(cache::loadAll); Defensive patterns
Strategy: validation
Validate before calling
// before calling cache.loadAll directly, ensure keys are homogeneous
boolean homogeneous(List<ColumnCardinalityCache.CacheKey> keys) {
var any = keys.get(0);
return keys.stream().allMatch(k ->
any.getSchema().equals(k.getSchema()) && any.getTable().equals(k.getTable())
&& any.getFamily().equals(k.getFamily()) && any.getQualifier().equals(k.getQualifier()));
} Type guard
boolean sameColumn(ColumnCardinalityCache.CacheKey a, ColumnCardinalityCache.CacheKey b) {
return a.getSchema().equals(b.getSchema()) && a.getTable().equals(b.getTable())
&& a.getFamily().equals(b.getFamily()) && a.getQualifier().equals(b.getQualifier());
} Try / catch
try {
cache.loadAll(keys);
} catch (PrestoException e) {
if (e.getErrorCode().getName().equals("FUNCTION_IMPLEMENTATION_ERROR")) {
// partition keys by schema/table/family/qualifier and load per group
} else {
throw e;
}
} Prevention
- Never call loadAll directly; use getCardinalities which batches keys correctly.
- If extending the connector, group cache keys by (schema, table, family, qualifier) before loadAll.
- Add a unit test asserting batch key homogeneity before invoking the loader.
When it happens
Trigger: A batch of cache keys with mixed schema/table/family/qualifier values is passed to loadAll - only possible via a code-level bug in how keys are grouped before invoking the cache, or by calling the cache loader directly with heterogeneous keys.
Common situations: Custom modifications or patches to ColumnCardinalityCache; calling loadAll directly in tests or tooling with arbitrary keys; a connector change that no longer partitions keys per column before invoking the cache.
Related errors
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/1c05d4ba4788ee59.
Report an issue: GitHub.