public int compare(Session session, PlanCostEstimate left, PlanCostEstimate right)
    {
        requireNonNull(session, "session is null");
        requireNonNull(left, "left is null");
        requireNonNull(right, "right is null");
        checkArgument(!left.hasUnknownComponents() && !right.hasUnknownComponents(), "cannot compare unknown costs");

        // TODO when one left.getMaxMemory() and right.getMaxMemory() exceeds query memory limit * configurable safety margin, choose the plan with lower memory usage

        double leftCost = left.getCpuCost() * cpuWeight
                + left.getMaxMemory() * memoryWeight
                + left.getNetworkCost() * networkWeight;

        double rightCost = right.getCpuCost() * cpuWeight
                + right.getMaxMemory() * memoryWeight
                + right.getNetworkCost() * networkWeight;

        return Double.compare(leftCost, rightCost);
    }
@Test
    public void testUnknownCost()
    {
        CostComparator costComparator = new CostComparator(1.0, 1.0, 1.0);
        Session session = testSessionBuilder().build();
        assertThrows(IllegalArgumentException.class, () -> costComparator.compare(session, PlanCostEstimate.zero(), PlanCostEstimate.unknown()));
        assertThrows(IllegalArgumentException.class, () -> costComparator.compare(session, PlanCostEstimate.unknown(), PlanCostEstimate.zero()));
        assertThrows(IllegalArgumentException.class, () -> costComparator.compare(session, PlanCostEstimate.unknown(), PlanCostEstimate.unknown()));
    }