{"record":{"id":"1c05d4ba4788ee59","repo":"prestodb/presto","slug":"function-implementation-error-1c05d4","errorCode":"FUNCTION_IMPLEMENTATION_ERROR","errorMessage":"loadAll called with a non-homogeneous collection of cache keys","messagePattern":"loadAll called with a non-homogeneous collection of cache keys","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"presto-accumulo/src/main/java/com/facebook/presto/accumulo/index/ColumnCardinalityCache.java","lineNumber":376,"sourceCode":"        }\n\n        @Override\n        public Map<CacheKey, Long> loadAll(Iterable<? extends CacheKey> keys)\n                throws Exception\n        {\n            int size = Iterables.size(keys);\n            if (size == 0) {\n                return ImmutableMap.of();\n            }\n\n            LOG.debug(\"Loading %s exact ranges from Accumulo\", size);\n\n            // In order to simplify the implementation, we are making a (safe) assumption\n            // that the CacheKeys will all contain the same combination of schema/table/family/qualifier\n            // This is asserted with the below implementation error just to make sure\n            CacheKey anyKey = stream(keys).findAny().get();\n            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()))) {\n                throw new PrestoException(FUNCTION_IMPLEMENTATION_ERROR, \"loadAll called with a non-homogeneous collection of cache keys\");\n            }\n\n            Map<Range, CacheKey> rangeToKey = stream(keys).collect(Collectors.toMap(CacheKey::getRange, Function.identity()));\n            LOG.debug(\"rangeToKey size is %s\", rangeToKey.size());\n\n            // Get metrics table name and the column family for the scanner\n            String metricsTable = getMetricsTableName(anyKey.getSchema(), anyKey.getTable());\n            Text columnFamily = new Text(getIndexColumnFamily(anyKey.getFamily().getBytes(UTF_8), anyKey.getQualifier().getBytes(UTF_8)).array());\n\n            BatchScanner scanner = connector.createBatchScanner(metricsTable, anyKey.getAuths(), 10);\n            try {\n                scanner.setRanges(stream(keys).map(CacheKey::getRange).collect(Collectors.toList()));\n                scanner.fetchColumn(columnFamily, CARDINALITY_CQ_AS_TEXT);\n\n                // Create a new map to hold our cardinalities for each range, returning a default of\n                // Zero for each non-existent Key\n                Map<CacheKey, Long> rangeValues = new HashMap<>();\n                stream(keys).forEach(key -> rangeValues.put(key, 0L));","sourceCodeStart":358,"sourceCodeEnd":394,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-accumulo/src/main/java/com/facebook/presto/accumulo/index/ColumnCardinalityCache.java#L358-L394","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ncache.loadAll(keys); // keys span multiple tables/columns\n// after\nMap<List<String>, List<CacheKey>> grouped = keys.stream()\n    .collect(Collectors.groupingBy(k -> Arrays.asList(k.getSchema(), k.getTable(), k.getFamily(), k.getQualifier())));\ngrouped.values().forEach(cache::loadAll);","handlingStrategy":"validation","validationCode":"// before calling cache.loadAll directly, ensure keys are homogeneous\nboolean homogeneous(List<ColumnCardinalityCache.CacheKey> keys) {\n    var any = keys.get(0);\n    return keys.stream().allMatch(k ->\n        any.getSchema().equals(k.getSchema()) && any.getTable().equals(k.getTable())\n        && any.getFamily().equals(k.getFamily()) && any.getQualifier().equals(k.getQualifier()));\n}","typeGuard":"boolean sameColumn(ColumnCardinalityCache.CacheKey a, ColumnCardinalityCache.CacheKey b) {\n    return a.getSchema().equals(b.getSchema()) && a.getTable().equals(b.getTable())\n        && a.getFamily().equals(b.getFamily()) && a.getQualifier().equals(b.getQualifier());\n}","tryCatchPattern":"try {\n    cache.loadAll(keys);\n} catch (PrestoException e) {\n    if (e.getErrorCode().getName().equals(\"FUNCTION_IMPLEMENTATION_ERROR\")) {\n        // partition keys by schema/table/family/qualifier and load per group\n    } else {\n        throw e;\n    }\n}","preventionTips":["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."],"tags":["accumulo","cache","internal-invariant","cardinality"],"backgroundTag":"non-homogeneous-batch","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}