0

I'm trying to load test a backend server and I'm parsing some config into a Label object. I'm trying to generate Cartesian product of Label objects which would be a Metric object.

Label {
   String name;
   String labe;
}

I have a List<List<Label>> labelList converted from the yaml config which need to be converted into List<Metric> via cartesian product. Here is an example.

[[name: "office_instance"value: "office_instance_0", name: "office_instance"value: "office_instance_1", name: "office_instance"value: "office_instance_2", name: "office_instance"value: "office_instance_3"], [name: "office_type"value: "office_type_0", name: "office_type"value: "office_type_1", name: "office_type"value: "office_type_2", name: "office_type"value: "office_type_3"], [name: "resource_type"value: "resource_type_0", name: "resource_type"value: "resource_type_1", name: "resource_type"value: "resource_type_2", name: "resource_type"value: "resource_type_3"], [name: "resource"value: "resource_0", name: "resource"value: "resource_1", name: "resource"value: "resource_2", name: "resource"value: "resource_3"], [name: "resource_group_name"value: "resource_group_name_0", name: "resource_group_name"value: "resource_group_name_1", name: "resource_group_name"value: "resource_group_name_2", name: "resource_group_name"value: "resource_group_name_3"], [name: "scope"value: "scope_0", name: "scope"value: "scope_1", name: "scope"value: "scope_2", name: "scope"value: "scope_3"]]

Since this is generated from a config, the code to generate the Timeseries needs to be dynamic. In this example the labelList is of size 5 and each individual list 4 Labels. So total number of metric objects will be 4 * 4 * 4 * 4 * 4.

Example of Metric object will be

[[ name: "office_instance"value: "office_instance_0", name: "office_type"value: "office_type_0", name: "resource_type"value: "resource_type_0", name: "resource"value: "resource_0", name: "resource_group_name"value: "resource_group_name_0", name: "scope"value: "scope_0"][name: "office_instance"value: "office_instance_0", name: "office_type"value: "office_type_0", name: "resource_type"value: "resource_type_0", name: "resource"value: "resource_0", name: "resource_group_name"value: "resource_group_name_0", name: "scope"value: "scope_1"]......]

There are two ways to generate a Metric is by either adding Label one at a time via addLabels method

 Metric.newBuilder().addLabels(Label obj).build()

or adding via addAllLabels method

 Metric.newBuilder().addAllLabels(List<Label> obj).build()

I tried to do it iteratively/recursively but it gets really complex and messy. I checked if there is a java8 alternative. If I'm able to get a List<Label> or Collection<Label> after the 1st pass, I could use the addAlLabels method to create the Metric Object and add it to the final List instead of creating the Metric object and adding each label

I referred to a few examples here Cartesian product of streams in Java 8 as stream (using streams only). but still couldn't figure it out. Any help would be appreciated.

EDIT: Found guava has cartesianProduct Api, which might be helpful not sure if its the most optimized.

Shrenik Gala
  • 283
  • 1
  • 7
  • 19

1 Answers1

1

The way I see it you need a recursive function. This is what I came up with:

public class Cartesian {

    public static void main(String[] args) {
        List<List<Label>> labelList = Arrays.asList(//
                Arrays.asList( //
                        new Label("office_instance", "office_instance_0"), //
                        new Label("office_instance", "office_instance_1") //
                ), //
                Arrays.asList( //
                        new Label("office_type", "office_type_0"), //
                        new Label("office_type", "office_type_1") //
                ), //
                Arrays.asList( //
                        new Label("resource_type", "resource_type_0"), //
                        new Label("resource_type", "resource_type_1") //
                )//

        );
        List<List<Label>> result = generateCombinations(labelList);

        System.out.println(result);
    }

    private static List<List<Label>> generateCombinations(List<List<Label>> labels) {
        List<List<Label>> result = new ArrayList<>();
        // for each label in first list
        // generate combinations of labels from the rest of the list
        for (Label label : labels.get(0)) {
            if (labels.size() > 1) {
                for (List<Label> entry : generateCombinations(labels.subList(1, labels.size()))) {
                    entry.add(label);
                    result.add(entry);
                }
            } else {
                // base case
                result.add(new ArrayList<>(Arrays.asList(label)));
            }
        }
        return result;
    }

    private static class Label {
        String name;
        String label;

        Label(String name, String label) {
            this.name = name;
            this.label = label;
        }

        @Override
        public String toString() {
            return "(name: " + name + ", label: " + label + ")";
        }
    }
}

I didn't use the full list for brevity.

Output (manually formatted):

[
    [
        (name: resource_type, label: resource_type_0),
        (name: office_type, label: office_type_0),
        (name: office_instance, label: office_instance_0)
    ],
    [
        (name: resource_type, label: resource_type_1),
        (name: office_type, label: office_type_0),
        (name: office_instance, label: office_instance_0)
    ],
    [
        (name: resource_type, label: resource_type_0),
        (name: office_type, label: office_type_1),
        (name: office_instance, label: office_instance_0)
    ],
    [
        (name: resource_type, label: resource_type_1),
        (name: office_type, label: office_type_1),
        (name: office_instance, label: office_instance_0)
    ],
    [
        (name: resource_type, label: resource_type_0),
        (name: office_type, label: office_type_0),
        (name: office_instance, label: office_instance_1)
    ],
    [
        (name: resource_type, label: resource_type_1),
        (name: office_type, label: office_type_0),
        (name: office_instance, label: office_instance_1)
    ],
    [
        (name: resource_type, label: resource_type_0),
        (name: office_type, label: office_type_1),
        (name: office_instance, label: office_instance_1)
    ],
    [
        (name: resource_type, label: resource_type_1),
        (name: office_type, label: office_type_1),
        (name: office_instance, label: office_instance_1)
    ]
]
  • 1
    thanks for posting your recursive solution, I had tried something similar but the time it takes to generate 1million Metrics is about 0.5s, I wanted to see if there is a much optimal solution probably using streams. – Shrenik Gala Aug 11 '19 at 21:45