Use a memoizing supplier instead of a looping thread to cache remote config entries
This commit is contained in:
		
							parent
							
								
									d89b4f7e95
								
							
						
					
					
						commit
						048e17c62b
					
				| 
						 | 
					@ -593,7 +593,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
 | 
				
			||||||
    environment.lifecycle().manage(accountDatabaseCrawler);
 | 
					    environment.lifecycle().manage(accountDatabaseCrawler);
 | 
				
			||||||
    environment.lifecycle().manage(directoryReconciliationAccountDatabaseCrawler);
 | 
					    environment.lifecycle().manage(directoryReconciliationAccountDatabaseCrawler);
 | 
				
			||||||
    environment.lifecycle().manage(deletedAccountsTableCrawler);
 | 
					    environment.lifecycle().manage(deletedAccountsTableCrawler);
 | 
				
			||||||
    environment.lifecycle().manage(remoteConfigsManager);
 | 
					 | 
				
			||||||
    environment.lifecycle().manage(messagesCache);
 | 
					    environment.lifecycle().manage(messagesCache);
 | 
				
			||||||
    environment.lifecycle().manage(messagePersister);
 | 
					    environment.lifecycle().manage(messagePersister);
 | 
				
			||||||
    environment.lifecycle().manage(clientPresenceManager);
 | 
					    environment.lifecycle().manage(clientPresenceManager);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,70 +5,26 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package org.whispersystems.textsecuregcm.storage;
 | 
					package org.whispersystems.textsecuregcm.storage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.common.annotations.VisibleForTesting;
 | 
					import com.google.common.base.Suppliers;
 | 
				
			||||||
import io.dropwizard.lifecycle.Managed;
 | 
					 | 
				
			||||||
import org.slf4j.Logger;
 | 
					 | 
				
			||||||
import org.slf4j.LoggerFactory;
 | 
					 | 
				
			||||||
import org.whispersystems.textsecuregcm.util.Util;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.LinkedList;
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.concurrent.TimeUnit;
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
import java.util.concurrent.atomic.AtomicReference;
 | 
					import java.util.function.Supplier;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class RemoteConfigsManager implements Managed {
 | 
					public class RemoteConfigsManager {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  private final Logger logger = LoggerFactory.getLogger(RemoteConfigsManager.class);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final RemoteConfigs remoteConfigs;
 | 
					  private final RemoteConfigs remoteConfigs;
 | 
				
			||||||
  private final long          sleepInterval;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final AtomicReference<List<RemoteConfig>> cachedConfigs = new AtomicReference<>(new LinkedList<>());
 | 
					  private final Supplier<List<RemoteConfig>> remoteConfigSupplier;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public RemoteConfigsManager(RemoteConfigs remoteConfigs) {
 | 
					  public RemoteConfigsManager(RemoteConfigs remoteConfigs) {
 | 
				
			||||||
    this(remoteConfigs, TimeUnit.SECONDS.toMillis(10));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @VisibleForTesting
 | 
					 | 
				
			||||||
  public RemoteConfigsManager(RemoteConfigs remoteConfigs, long sleepInterval) {
 | 
					 | 
				
			||||||
    this.remoteConfigs = remoteConfigs;
 | 
					    this.remoteConfigs = remoteConfigs;
 | 
				
			||||||
    this.sleepInterval = sleepInterval;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					    remoteConfigSupplier =
 | 
				
			||||||
  public void start() {
 | 
					        Suppliers.memoizeWithExpiration(remoteConfigs::getAll, 10, TimeUnit.SECONDS);
 | 
				
			||||||
    refreshCache();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    new Thread(() -> {
 | 
					 | 
				
			||||||
      while (true) {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
          refreshCache();
 | 
					 | 
				
			||||||
        } catch (Throwable t) {
 | 
					 | 
				
			||||||
          logger.warn("Error updating remote configs cache", t);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Util.sleep(sleepInterval);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }).start();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private void refreshCache() {
 | 
					 | 
				
			||||||
    this.cachedConfigs.set(remoteConfigs.getAll());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    synchronized (this.cachedConfigs) {
 | 
					 | 
				
			||||||
      this.cachedConfigs.notifyAll();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @VisibleForTesting
 | 
					 | 
				
			||||||
  void waitForCacheRefresh() throws InterruptedException {
 | 
					 | 
				
			||||||
    synchronized (this.cachedConfigs) {
 | 
					 | 
				
			||||||
      this.cachedConfigs.wait();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public List<RemoteConfig> getAll() {
 | 
					  public List<RemoteConfig> getAll() {
 | 
				
			||||||
    return cachedConfigs.get();
 | 
					    return remoteConfigSupplier.get();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void set(RemoteConfig config) {
 | 
					  public void set(RemoteConfig config) {
 | 
				
			||||||
| 
						 | 
					@ -78,9 +34,4 @@ public class RemoteConfigsManager implements Managed {
 | 
				
			||||||
  public void delete(String name) {
 | 
					  public void delete(String name) {
 | 
				
			||||||
    remoteConfigs.delete(name);
 | 
					    remoteConfigs.delete(name);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public void stop() throws Exception {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,21 +29,18 @@ public class RemoteConfigsManagerTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Before
 | 
					  @Before
 | 
				
			||||||
  public void setup() {
 | 
					  public void setup() {
 | 
				
			||||||
    RemoteConfigs remoteConfigs = new RemoteConfigs(new FaultTolerantDatabase("remote_configs-test", Jdbi.create(db.getTestDatabase()), new CircuitBreakerConfiguration()));
 | 
					    this.remoteConfigs = new RemoteConfigsManager(new RemoteConfigs(
 | 
				
			||||||
    this.remoteConfigs = new RemoteConfigsManager(remoteConfigs, 500);
 | 
					        new FaultTolerantDatabase("remote_configs-test", Jdbi.create(db.getTestDatabase()), new CircuitBreakerConfiguration())));
 | 
				
			||||||
    this.remoteConfigs.start();
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
  public void testUpdate() throws InterruptedException {
 | 
					  public void testUpdate() {
 | 
				
			||||||
    remoteConfigs.set(new RemoteConfig("android.stickers", 50, Set.of(AuthHelper.VALID_UUID), "FALSE", "TRUE", null));
 | 
					    remoteConfigs.set(new RemoteConfig("android.stickers", 50, Set.of(AuthHelper.VALID_UUID), "FALSE", "TRUE", null));
 | 
				
			||||||
    remoteConfigs.set(new RemoteConfig("value.sometimes", 50, Set.of(), "bar", "baz", null));
 | 
					    remoteConfigs.set(new RemoteConfig("value.sometimes", 50, Set.of(), "bar", "baz", null));
 | 
				
			||||||
    remoteConfigs.set(new RemoteConfig("ios.stickers", 50, Set.of(), "FALSE", "TRUE", null));
 | 
					    remoteConfigs.set(new RemoteConfig("ios.stickers", 50, Set.of(), "FALSE", "TRUE", null));
 | 
				
			||||||
    remoteConfigs.set(new RemoteConfig("ios.stickers", 75, Set.of(), "FALSE", "TRUE", null));
 | 
					    remoteConfigs.set(new RemoteConfig("ios.stickers", 75, Set.of(), "FALSE", "TRUE", null));
 | 
				
			||||||
    remoteConfigs.set(new RemoteConfig("value.sometimes", 25, Set.of(AuthHelper.VALID_UUID), "abc", "def", null));
 | 
					    remoteConfigs.set(new RemoteConfig("value.sometimes", 25, Set.of(AuthHelper.VALID_UUID), "abc", "def", null));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    remoteConfigs.waitForCacheRefresh();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    List<RemoteConfig> results = remoteConfigs.getAll();
 | 
					    List<RemoteConfig> results = remoteConfigs.getAll();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assertThat(results.size()).isEqualTo(3);
 | 
					    assertThat(results.size()).isEqualTo(3);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue