public synchronized void reportData(String clusterName, UriProperties property, boolean fromNewLb)
  {
    ClusterMatchRecord cluster = _clusters.computeIfAbsent(clusterName, k -> new ClusterMatchRecord());

    if (fromNewLb)
    {
      cluster._newLb = property;
    }
    else
    {
      cluster._oldLb = property;
    }

    _totalUris -= cluster._uris;
    _matchedUris -= cluster._matched;

    LOG.debug("Updated URI properties for cluster {}:\nOld LB: {}\nNew LB: {}",
        clusterName, cluster._oldLb, cluster._newLb);

    if (cluster._oldLb == null && cluster._newLb == null)
    {
      _clusters.remove(clusterName);
      updateJmxMetrics(clusterName, null);
      return;
    }

    cluster._matched = 0;

    if (cluster._oldLb == null || cluster._newLb == null)
    {
      LOG.debug("Added new URI properties for {} for {} LB.", clusterName, fromNewLb ? "New" : "Old");

      cluster._uris = (cluster._oldLb == null) ? cluster._newLb.Uris().size() : cluster._oldLb.Uris().size();
      _totalUris += cluster._uris;

      updateJmxMetrics(clusterName, cluster);
      return;
    }

    cluster._uris = cluster._oldLb.Uris().size();
    Set<URI> newLbUris = new HashSet<>(cluster._newLb.Uris());

    for (URI uri : cluster._oldLb.Uris())
    {
      if (!newLbUris.remove(uri))
      {
        continue;
      }

      if (compareURI(uri, cluster._oldLb, cluster._newLb))
      {
        cluster._matched++;
      }
    }
    // add the remaining unmatched URIs in newLbUris to the uri count
    cluster._uris += newLbUris.size();

    if (cluster._matched != cluster._uris)
    {
      infoOrDebugIfLimited(
          "Mismatched uri properties for cluster {} (match score: {}, total uris: {}):\nOld LB: {}\nNew LB: {}",
          clusterName, (double) cluster._matched / (double) cluster._uris, cluster._uris, cluster._oldLb,
          cluster._newLb);
    }
    else
    {
      LOG.debug("Matched uri properties for cluster {} (matched {} out of {} URIs)", clusterName,
          cluster._matched, cluster._uris);
    }

    _totalUris += cluster._uris;
    _matchedUris += cluster._matched;

    updateJmxMetrics(clusterName, cluster);
  }
@Test
  public void testReportData() {
    UriPropertiesDualReadMonitorTestFixture fixture = new UriPropertiesDualReadMonitorTestFixture();
    UriPropertiesDualReadMonitor monitor = fixture.getMonitor();

    // new lb has uri 1
    monitor.reportData(CLUSTER_1, URI_PROPERTIES_1, true);
    verifyJmxMetricParams(fixture, CLUSTER_1,
        new ClusterMatchRecord(null, URI_PROPERTIES_1, 1, 0),
        0.0);

    // old lb has uri 2
    monitor.reportData(CLUSTER_1, URI_PROPERTIES_2, false);
    verifyJmxMetricParams(fixture, CLUSTER_1,
        new ClusterMatchRecord(URI_PROPERTIES_2, URI_PROPERTIES_1, 2, 0),
        0.0);

    // old lb updated with both uri 1 and 2
    monitor.reportData(CLUSTER_1, URI_PROPERTIES_URI_1_AND_2, false);
    verifyJmxMetricParams(fixture, CLUSTER_1,
        new ClusterMatchRecord(URI_PROPERTIES_URI_1_AND_2, URI_PROPERTIES_1, 2, 1),
        0.5);

    // new lb updated with both uri 1 and 2
    monitor.reportData(CLUSTER_1, URI_PROPERTIES_URI_1_AND_2, true);
    verifyJmxMetricParams(fixture, CLUSTER_1,
        new ClusterMatchRecord(URI_PROPERTIES_URI_1_AND_2, URI_PROPERTIES_URI_1_AND_2, 2, 2),
        1.0);

    // add data for cluster 2, old lb with uri 3 and 4
    monitor.reportData(CLUSTER_2, URI_PROPERTIES_URI_3_AND_4, false);
    assertEquals(monitor.getTotalUris(), 4);
    assertEquals(monitor.getMatchedUris(), 2);
    verifyJmxMetricParams(fixture, CLUSTER_2,
        new ClusterMatchRecord(URI_PROPERTIES_URI_3_AND_4, null, 2, 0),
        0.5);

    // new lb updated with uri 3 with different uri specific properties and uri 4 with different weight
    monitor.reportData(CLUSTER_2, URI_PROPERTIES_URI_3_DIFF_SPECIFIC_PROPERTIES_AND_4_DIFF_WEIGHT, true);
    assertEquals(monitor.getTotalUris(), 4);
    assertEquals(monitor.getMatchedUris(), 2);
    verifyJmxMetricParams(fixture, CLUSTER_2,
        new ClusterMatchRecord(URI_PROPERTIES_URI_3_AND_4, URI_PROPERTIES_URI_3_DIFF_SPECIFIC_PROPERTIES_AND_4_DIFF_WEIGHT,
            2, 0),
        0.5);

    // old lb updated with uri 3 with still different uri specific properties and uri 4 with same weight as new lb
    monitor.reportData(CLUSTER_2, URI_PROPERTIES_URI_3_ANOTHER_DIFF_SPECIFIC_PROPERTIES_AND_4_DIFF_WEIGHT, false);
    assertEquals(monitor.getTotalUris(), 4);
    assertEquals(monitor.getMatchedUris(), 3);
    verifyJmxMetricParams(fixture, CLUSTER_2,
        new ClusterMatchRecord(URI_PROPERTIES_URI_3_ANOTHER_DIFF_SPECIFIC_PROPERTIES_AND_4_DIFF_WEIGHT,
            URI_PROPERTIES_URI_3_DIFF_SPECIFIC_PROPERTIES_AND_4_DIFF_WEIGHT,
            2, 1),
        0.75);

    // delete both lbs data for cluster 2
    monitor.reportData(CLUSTER_2, null, true);
    monitor.reportData(CLUSTER_2, null, false);
    verifyJmxMetricParams(fixture, CLUSTER_2, null, 1.0);
  }