class MetricsRecordHandler extends RecordHandler {

    private void readMetricsWithConstraint(BlockSpiller blockSpiller, ReadRecordsRequest request, QueryStatusChecker queryStatusChecker)
            throws TimeoutException
    {
        ListMetricsRequest listMetricsRequest = new ListMetricsRequest();
        MetricUtils.pushDownPredicate(request.getConstraints(), listMetricsRequest);
        String prevToken;
        Set<String> requiredFields = new HashSet<>();
        request.getSchema().getFields().stream().forEach(next -> requiredFields.add(next.getName()));
        ValueSet dimensionNameConstraint = request.getConstraints().getSummary().get(DIMENSION_NAME_FIELD);
        ValueSet dimensionValueConstraint = request.getConstraints().getSummary().get(DIMENSION_VALUE_FIELD);
        do {
            prevToken = listMetricsRequest.getNextToken();
            ListMetricsResult result = invoker.invoke(() -> metrics.listMetrics(listMetricsRequest));
            for (Metric nextMetric : result.getMetrics()) {
                blockSpiller.writeRows((Block block, int row) -> {
                    boolean matches = MetricUtils.applyMetricConstraints(blockSpiller.getConstraintEvaluator(), nextMetric, null);
                    if (matches) {
                        matches &= block.offerValue(METRIC_NAME_FIELD, row, nextMetric.getMetricName());
                        matches &= block.offerValue(NAMESPACE_FIELD, row, nextMetric.getNamespace());
                        matches &= block.offerComplexValue(STATISTIC_FIELD, row, DEFAULT, STATISTICS);

                        matches &= block.offerComplexValue(DIMENSIONS_FIELD,
                                row,
                                (Field field, Object val) -> {
                                    if (field.getName().equals(DIMENSION_NAME_FIELD)) {
                                        return ((Dimension) val).getName();
                                    }
                                    else if (field.getName().equals(DIMENSION_VALUE_FIELD)) {
                                        return ((Dimension) val).getValue();
                                    }

                                    throw new RuntimeException("Unexpected field " + field.getName());
                                },
                                nextMetric.getDimensions());

                        //This field is 'faked' in that we just use it as a convenient way to filter single dimensions. As such
                        //we always populate it with the value of the filter if the constraint passed and the filter was singleValue
                        String dimName = (dimensionNameConstraint == null || !dimensionNameConstraint.isSingleValue())
                                ? null : (dimensionNameConstraint.getSingleValue().toString());
                        matches &= block.offerValue(DIMENSION_NAME_FIELD, row, dimName);

                        //This field is 'faked' in that we just use it as a convenient way to filter single dimensions. As such
                        //we always populate it with the value of the filter if the constraint passed and the filter was singleValue
                        String dimValue = (dimensionValueConstraint == null || !dimensionValueConstraint.isSingleValue())
                                ? null : dimensionValueConstraint.getSingleValue().toString();
                        matches &= block.offerValue(DIMENSION_VALUE_FIELD, row, dimValue);
                    }
                    return matches ? 1 : 0;
                });
            }
            listMetricsRequest.setNextToken(result.getNextToken());
        }
        while (listMetricsRequest.getNextToken() != null && !listMetricsRequest.getNextToken().equalsIgnoreCase(prevToken) && queryStatusChecker.isQueryRunning());
    }

    public  MetricsRecordHandler();
    @VisibleForTesting protected  MetricsRecordHandler(AmazonS3 amazonS3, AWSSecretsManager secretsManager, AmazonAthena athena, AmazonCloudWatch metrics);

}