@Override
  public List<ServiceInstance> getInstances(String serviceName) {
    // put serviceName as key to map,
    // then the task use it to read service instances from database
    this.serviceName2ServiceInstances.computeIfAbsent(serviceName, this::readFromDatabase);
    // get from cache
    return this.serviceName2ServiceInstances.getOrDefault(serviceName, Collections.emptyList());
  }
@Test
  void getInstances_from_cache() {
    DatabaseDiscoveryClient client = Mockito.mock(DatabaseDiscoveryClient.class);
    Mockito.when(client.getInstances("a-service"))
        .thenReturn(
            Arrays.asList(
                newServiceInstance("a-service", "http://10.240.34.56:8080/", "beijing"),
                newServiceInstance("a-service", "http://10.240.34.56:8081/", "beijing")
            )
        );
    Mockito.when(client.getInstances("b-service"))
        .thenReturn(
            Arrays.asList(
                newServiceInstance("b-service", "http://10.240.56.78:8080/", "shanghai"),
                newServiceInstance("b-service", "http://10.240.56.78:8081/", "shanghai"),
                newServiceInstance("b-service", "http://10.240.56.78:8082/", "shanghai")
            )
        );

    DatabaseDiscoveryClientMemoryCacheDecoratorImpl decorator
        = new DatabaseDiscoveryClientMemoryCacheDecoratorImpl(client);
    assertEquals(2, decorator.getInstances("a-service").size());
    assertEquals(2, decorator.getInstances("a-service").size());
    assertEquals(3, decorator.getInstances("b-service").size());
    assertEquals(3, decorator.getInstances("b-service").size());

    // only invoke 1 times because always read from cache
    Mockito.verify(client, Mockito.times(1)).getInstances("a-service");
    Mockito.verify(client, Mockito.times(1)).getInstances("b-service");
  }