Remove `AccountCrawler` (and `doPeriodicWork`) from `WhisperServerService`
This commit is contained in:
parent
f26bc70b59
commit
f8fefe2e5e
|
@ -155,10 +155,6 @@ import org.whispersystems.textsecuregcm.spam.RateLimitChallengeListener;
|
||||||
import org.whispersystems.textsecuregcm.spam.ReportSpamTokenProvider;
|
import org.whispersystems.textsecuregcm.spam.ReportSpamTokenProvider;
|
||||||
import org.whispersystems.textsecuregcm.spam.ScoreThresholdProvider;
|
import org.whispersystems.textsecuregcm.spam.ScoreThresholdProvider;
|
||||||
import org.whispersystems.textsecuregcm.spam.SpamFilter;
|
import org.whispersystems.textsecuregcm.spam.SpamFilter;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountCleaner;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawler;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerCache;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerListener;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountLockManager;
|
import org.whispersystems.textsecuregcm.storage.AccountLockManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.Accounts;
|
import org.whispersystems.textsecuregcm.storage.Accounts;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
|
@ -171,12 +167,10 @@ import org.whispersystems.textsecuregcm.storage.MessagePersister;
|
||||||
import org.whispersystems.textsecuregcm.storage.MessagesCache;
|
import org.whispersystems.textsecuregcm.storage.MessagesCache;
|
||||||
import org.whispersystems.textsecuregcm.storage.MessagesDynamoDb;
|
import org.whispersystems.textsecuregcm.storage.MessagesDynamoDb;
|
||||||
import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.NonNormalizedAccountCrawlerListener;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.PhoneNumberIdentifiers;
|
import org.whispersystems.textsecuregcm.storage.PhoneNumberIdentifiers;
|
||||||
import org.whispersystems.textsecuregcm.storage.Profiles;
|
import org.whispersystems.textsecuregcm.storage.Profiles;
|
||||||
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.PushChallengeDynamoDb;
|
import org.whispersystems.textsecuregcm.storage.PushChallengeDynamoDb;
|
||||||
import org.whispersystems.textsecuregcm.storage.PushFeedbackProcessor;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.RedeemedReceiptsManager;
|
import org.whispersystems.textsecuregcm.storage.RedeemedReceiptsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswords;
|
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswords;
|
||||||
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
||||||
|
@ -386,10 +380,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
.executorService(name(getClass(), "secureValueRecoveryService-%d")).maxThreads(1).minThreads(1).build();
|
.executorService(name(getClass(), "secureValueRecoveryService-%d")).maxThreads(1).minThreads(1).build();
|
||||||
ExecutorService storageServiceExecutor = environment.lifecycle()
|
ExecutorService storageServiceExecutor = environment.lifecycle()
|
||||||
.executorService(name(getClass(), "storageService-%d")).maxThreads(1).minThreads(1).build();
|
.executorService(name(getClass(), "storageService-%d")).maxThreads(1).minThreads(1).build();
|
||||||
ExecutorService accountDeletionExecutor = environment.lifecycle()
|
|
||||||
.executorService(name(getClass(), "accountCleaner-%d")).maxThreads(16).minThreads(16).build();
|
|
||||||
ExecutorService pushFeedbackUpdateExecutor = environment.lifecycle()
|
|
||||||
.executorService(name(getClass(), "pushFeedback-%d")).maxThreads(4).minThreads(4).build();
|
|
||||||
|
|
||||||
Scheduler messageDeliveryScheduler = Schedulers.fromExecutorService(
|
Scheduler messageDeliveryScheduler = Schedulers.fromExecutorService(
|
||||||
ExecutorServiceMetrics.monitor(Metrics.globalRegistry,
|
ExecutorServiceMetrics.monitor(Metrics.globalRegistry,
|
||||||
|
@ -562,31 +552,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
Optional.empty());
|
Optional.empty());
|
||||||
ChangeNumberManager changeNumberManager = new ChangeNumberManager(messageSender, accountsManager);
|
ChangeNumberManager changeNumberManager = new ChangeNumberManager(messageSender, accountsManager);
|
||||||
|
|
||||||
AccountDatabaseCrawlerCache accountCleanerAccountDatabaseCrawlerCache =
|
|
||||||
new AccountDatabaseCrawlerCache(cacheCluster, AccountDatabaseCrawlerCache.ACCOUNT_CLEANER_PREFIX);
|
|
||||||
AccountDatabaseCrawler accountCleanerAccountDatabaseCrawler = new AccountDatabaseCrawler("Account cleaner crawler",
|
|
||||||
accountsManager,
|
|
||||||
accountCleanerAccountDatabaseCrawlerCache,
|
|
||||||
List.of(new AccountCleaner(accountsManager, accountDeletionExecutor)),
|
|
||||||
config.getAccountDatabaseCrawlerConfiguration().getChunkSize(),
|
|
||||||
dynamicConfigurationManager
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO listeners must be ordered so that ones that directly update accounts come last, so that read-only ones are not working with stale data
|
|
||||||
final List<AccountDatabaseCrawlerListener> accountDatabaseCrawlerListeners = List.of(
|
|
||||||
new NonNormalizedAccountCrawlerListener(accountsManager, metricsCluster),
|
|
||||||
// PushFeedbackProcessor may update device properties
|
|
||||||
new PushFeedbackProcessor(accountsManager, pushFeedbackUpdateExecutor));
|
|
||||||
|
|
||||||
AccountDatabaseCrawlerCache accountDatabaseCrawlerCache = new AccountDatabaseCrawlerCache(cacheCluster,
|
|
||||||
AccountDatabaseCrawlerCache.GENERAL_PURPOSE_PREFIX);
|
|
||||||
AccountDatabaseCrawler accountDatabaseCrawler = new AccountDatabaseCrawler("General-purpose account crawler",
|
|
||||||
accountsManager,
|
|
||||||
accountDatabaseCrawlerCache, accountDatabaseCrawlerListeners,
|
|
||||||
config.getAccountDatabaseCrawlerConfiguration().getChunkSize(),
|
|
||||||
dynamicConfigurationManager
|
|
||||||
);
|
|
||||||
|
|
||||||
HttpClient currencyClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).connectTimeout(Duration.ofSeconds(10)).build();
|
HttpClient currencyClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).connectTimeout(Duration.ofSeconds(10)).build();
|
||||||
FixerClient fixerClient = new FixerClient(currencyClient, config.getPaymentsServiceConfiguration().fixerApiKey().value());
|
FixerClient fixerClient = new FixerClient(currencyClient, config.getPaymentsServiceConfiguration().fixerApiKey().value());
|
||||||
CoinMarketCapClient coinMarketCapClient = new CoinMarketCapClient(currencyClient, config.getPaymentsServiceConfiguration().coinMarketCapApiKey().value(), config.getPaymentsServiceConfiguration().coinMarketCapCurrencyIds());
|
CoinMarketCapClient coinMarketCapClient = new CoinMarketCapClient(currencyClient, config.getPaymentsServiceConfiguration().coinMarketCapApiKey().value(), config.getPaymentsServiceConfiguration().coinMarketCapCurrencyIds());
|
||||||
|
@ -596,8 +561,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
environment.lifecycle().manage(apnSender);
|
environment.lifecycle().manage(apnSender);
|
||||||
environment.lifecycle().manage(apnPushNotificationScheduler);
|
environment.lifecycle().manage(apnPushNotificationScheduler);
|
||||||
environment.lifecycle().manage(provisioningManager);
|
environment.lifecycle().manage(provisioningManager);
|
||||||
environment.lifecycle().manage(accountDatabaseCrawler);
|
|
||||||
environment.lifecycle().manage(accountCleanerAccountDatabaseCrawler);
|
|
||||||
environment.lifecycle().manage(messagesCache);
|
environment.lifecycle().manage(messagesCache);
|
||||||
environment.lifecycle().manage(messagePersister);
|
environment.lifecycle().manage(messagePersister);
|
||||||
environment.lifecycle().manage(clientPresenceManager);
|
environment.lifecycle().manage(clientPresenceManager);
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023 Signal Messenger, LLC
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.configuration.dynamic;
|
|
||||||
|
|
||||||
public record DynamicAccountDatabaseCrawlerConfiguration(boolean periodicWorkEnabled, boolean crawlAllEnabled) {
|
|
||||||
|
|
||||||
}
|
|
|
@ -56,16 +56,10 @@ public class DynamicConfiguration {
|
||||||
@Valid
|
@Valid
|
||||||
DynamicMessagePersisterConfiguration messagePersister = new DynamicMessagePersisterConfiguration();
|
DynamicMessagePersisterConfiguration messagePersister = new DynamicMessagePersisterConfiguration();
|
||||||
|
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@Valid
|
@Valid
|
||||||
DynamicRateLimitPolicy rateLimitPolicy = new DynamicRateLimitPolicy(false);
|
DynamicRateLimitPolicy rateLimitPolicy = new DynamicRateLimitPolicy(false);
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
@Valid
|
|
||||||
DynamicAccountDatabaseCrawlerConfiguration accountDatabaseCrawler = new DynamicAccountDatabaseCrawlerConfiguration(
|
|
||||||
true, false);
|
|
||||||
|
|
||||||
public Optional<DynamicExperimentEnrollmentConfiguration> getExperimentEnrollmentConfiguration(
|
public Optional<DynamicExperimentEnrollmentConfiguration> getExperimentEnrollmentConfiguration(
|
||||||
final String experimentName) {
|
final String experimentName) {
|
||||||
return Optional.ofNullable(experiments.get(experimentName));
|
return Optional.ofNullable(experiments.get(experimentName));
|
||||||
|
@ -112,7 +106,4 @@ public class DynamicConfiguration {
|
||||||
return rateLimitPolicy;
|
return rateLimitPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamicAccountDatabaseCrawlerConfiguration getAccountDatabaseCrawlerConfiguration() {
|
|
||||||
return accountDatabaseCrawler;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,21 +9,15 @@ import static com.codahale.metrics.MetricRegistry.name;
|
||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
import com.codahale.metrics.SharedMetricRegistries;
|
import com.codahale.metrics.SharedMetricRegistries;
|
||||||
import com.codahale.metrics.Timer;
|
import com.codahale.metrics.Timer;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
import io.dropwizard.lifecycle.Managed;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.util.Constants;
|
import org.whispersystems.textsecuregcm.util.Constants;
|
||||||
import org.whispersystems.textsecuregcm.util.Util;
|
|
||||||
|
|
||||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||||
public class AccountDatabaseCrawler implements Managed, Runnable {
|
public class AccountDatabaseCrawler {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(AccountDatabaseCrawler.class);
|
private static final Logger logger = LoggerFactory.getLogger(AccountDatabaseCrawler.class);
|
||||||
private static final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
|
private static final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
|
||||||
|
@ -32,7 +26,6 @@ public class AccountDatabaseCrawler implements Managed, Runnable {
|
||||||
name(AccountDatabaseCrawler.class, "processChunk"));
|
name(AccountDatabaseCrawler.class, "processChunk"));
|
||||||
|
|
||||||
private static final long WORKER_TTL_MS = 120_000L;
|
private static final long WORKER_TTL_MS = 120_000L;
|
||||||
private static final long CHUNK_INTERVAL_MILLIS = Duration.ofSeconds(2).toMillis();
|
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final AccountsManager accounts;
|
private final AccountsManager accounts;
|
||||||
|
@ -41,58 +34,17 @@ public class AccountDatabaseCrawler implements Managed, Runnable {
|
||||||
private final AccountDatabaseCrawlerCache cache;
|
private final AccountDatabaseCrawlerCache cache;
|
||||||
private final List<AccountDatabaseCrawlerListener> listeners;
|
private final List<AccountDatabaseCrawlerListener> listeners;
|
||||||
|
|
||||||
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
|
||||||
|
|
||||||
private AtomicBoolean running = new AtomicBoolean(false);
|
|
||||||
private boolean finished;
|
|
||||||
|
|
||||||
public AccountDatabaseCrawler(final String name,
|
public AccountDatabaseCrawler(final String name,
|
||||||
AccountsManager accounts,
|
AccountsManager accounts,
|
||||||
AccountDatabaseCrawlerCache cache,
|
AccountDatabaseCrawlerCache cache,
|
||||||
List<AccountDatabaseCrawlerListener> listeners,
|
List<AccountDatabaseCrawlerListener> listeners,
|
||||||
int chunkSize,
|
int chunkSize) {
|
||||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.accounts = accounts;
|
this.accounts = accounts;
|
||||||
this.chunkSize = chunkSize;
|
this.chunkSize = chunkSize;
|
||||||
this.workerId = UUID.randomUUID().toString();
|
this.workerId = UUID.randomUUID().toString();
|
||||||
this.cache = cache;
|
this.cache = cache;
|
||||||
this.listeners = listeners;
|
this.listeners = listeners;
|
||||||
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void start() {
|
|
||||||
running.set(true);
|
|
||||||
new Thread(this).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void stop() {
|
|
||||||
running.set(false);
|
|
||||||
notifyAll();
|
|
||||||
while (!finished) {
|
|
||||||
Util.wait(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
|
|
||||||
while (running.get()) {
|
|
||||||
try {
|
|
||||||
doPeriodicWork();
|
|
||||||
sleepWhileRunning(CHUNK_INTERVAL_MILLIS);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
logger.warn("{}: error in database crawl: {}: {}", name, t.getClass().getSimpleName(), t.getMessage(), t);
|
|
||||||
Util.sleep(10000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (this) {
|
|
||||||
finished = true;
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void crawlAllAccounts() {
|
public void crawlAllAccounts() {
|
||||||
|
@ -101,7 +53,7 @@ public class AccountDatabaseCrawler implements Managed, Runnable {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Optional<UUID> fromUuid = cache.getLastUuid();
|
Optional<UUID> fromUuid = getLastUuid();
|
||||||
|
|
||||||
if (fromUuid.isEmpty()) {
|
if (fromUuid.isEmpty()) {
|
||||||
logger.info("{}: Started crawl", name);
|
logger.info("{}: Started crawl", name);
|
||||||
|
@ -110,83 +62,26 @@ public class AccountDatabaseCrawler implements Managed, Runnable {
|
||||||
logger.info("{}: Resuming crawl", name);
|
logger.info("{}: Resuming crawl", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
AccountCrawlChunk chunkAccounts;
|
||||||
AccountCrawlChunk chunkAccounts;
|
do {
|
||||||
do {
|
try (Timer.Context timer = processChunkTimer.time()) {
|
||||||
if (!dynamicConfigurationManager.getConfiguration().getAccountDatabaseCrawlerConfiguration()
|
logger.debug("{}: Processing chunk", name);
|
||||||
.crawlAllEnabled()) {
|
chunkAccounts = readChunk(fromUuid, chunkSize);
|
||||||
logger.warn("Exiting crawl - not enabled by dynamic configuration");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try (Timer.Context timer = processChunkTimer.time()) {
|
|
||||||
logger.debug("{}: Processing chunk", name);
|
|
||||||
chunkAccounts = readChunk(fromUuid, chunkSize);
|
|
||||||
|
|
||||||
for (AccountDatabaseCrawlerListener listener : listeners) {
|
|
||||||
listener.timeAndProcessCrawlChunk(fromUuid, chunkAccounts.getAccounts());
|
|
||||||
}
|
|
||||||
fromUuid = chunkAccounts.getLastUuid();
|
|
||||||
cacheLastUuid(fromUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
} while (!chunkAccounts.getAccounts().isEmpty());
|
|
||||||
|
|
||||||
logger.info("{}: Finished crawl", name);
|
|
||||||
listeners.forEach(AccountDatabaseCrawlerListener::onCrawlEnd);
|
|
||||||
|
|
||||||
} catch (AccountDatabaseCrawlerRestartException e) {
|
|
||||||
logger.warn("Crawl stopped", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
cache.releaseActiveWork(workerId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void doPeriodicWork() {
|
|
||||||
if (!dynamicConfigurationManager.getConfiguration().getAccountDatabaseCrawlerConfiguration()
|
|
||||||
.periodicWorkEnabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cache.claimActiveWork(workerId, WORKER_TTL_MS)) {
|
|
||||||
try {
|
|
||||||
processChunk();
|
|
||||||
} finally {
|
|
||||||
cache.releaseActiveWork(workerId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processChunk() {
|
|
||||||
|
|
||||||
try (Timer.Context timer = processChunkTimer.time()) {
|
|
||||||
|
|
||||||
final Optional<UUID> fromUuid = getLastUuid();
|
|
||||||
|
|
||||||
if (fromUuid.isEmpty()) {
|
|
||||||
logger.info("{}: Started crawl", name);
|
|
||||||
listeners.forEach(AccountDatabaseCrawlerListener::onCrawlStart);
|
|
||||||
}
|
|
||||||
|
|
||||||
final AccountCrawlChunk chunkAccounts = readChunk(fromUuid, chunkSize);
|
|
||||||
|
|
||||||
if (chunkAccounts.getAccounts().isEmpty()) {
|
|
||||||
logger.info("{}: Finished crawl", name);
|
|
||||||
listeners.forEach(AccountDatabaseCrawlerListener::onCrawlEnd);
|
|
||||||
cacheLastUuid(Optional.empty());
|
|
||||||
} else {
|
|
||||||
logger.debug("{}: Processing chunk", name);
|
|
||||||
try {
|
|
||||||
for (AccountDatabaseCrawlerListener listener : listeners) {
|
for (AccountDatabaseCrawlerListener listener : listeners) {
|
||||||
listener.timeAndProcessCrawlChunk(fromUuid, chunkAccounts.getAccounts());
|
listener.timeAndProcessCrawlChunk(fromUuid, chunkAccounts.getAccounts());
|
||||||
}
|
}
|
||||||
cacheLastUuid(chunkAccounts.getLastUuid());
|
fromUuid = chunkAccounts.getLastUuid();
|
||||||
} catch (AccountDatabaseCrawlerRestartException e) {
|
cacheLastUuid(fromUuid);
|
||||||
cacheLastUuid(Optional.empty());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
} while (!chunkAccounts.getAccounts().isEmpty());
|
||||||
|
|
||||||
|
logger.info("{}: Finished crawl", name);
|
||||||
|
listeners.forEach(AccountDatabaseCrawlerListener::onCrawlEnd);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
cache.releaseActiveWork(workerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,10 +108,4 @@ public class AccountDatabaseCrawler implements Managed, Runnable {
|
||||||
cache.setLastUuid(lastUuid);
|
cache.setLastUuid(lastUuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void sleepWhileRunning(long delayMs) {
|
|
||||||
if (running.get()) {
|
|
||||||
Util.wait(this, delayMs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,19 +16,19 @@ import org.whispersystems.textsecuregcm.util.Constants;
|
||||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||||
public abstract class AccountDatabaseCrawlerListener {
|
public abstract class AccountDatabaseCrawlerListener {
|
||||||
|
|
||||||
private Timer processChunkTimer;
|
private final Timer processChunkTimer;
|
||||||
|
|
||||||
abstract public void onCrawlStart();
|
abstract public void onCrawlStart();
|
||||||
|
|
||||||
abstract public void onCrawlEnd();
|
abstract public void onCrawlEnd();
|
||||||
|
|
||||||
abstract protected void onCrawlChunk(Optional<UUID> fromUuid, List<Account> chunkAccounts) throws AccountDatabaseCrawlerRestartException;
|
abstract protected void onCrawlChunk(Optional<UUID> fromUuid, List<Account> chunkAccounts);
|
||||||
|
|
||||||
public AccountDatabaseCrawlerListener() {
|
public AccountDatabaseCrawlerListener() {
|
||||||
processChunkTimer = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME).timer(name(AccountDatabaseCrawlerListener.class, "processChunk", getClass().getSimpleName()));
|
processChunkTimer = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME).timer(name(AccountDatabaseCrawlerListener.class, "processChunk", getClass().getSimpleName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void timeAndProcessCrawlChunk(Optional<UUID> fromUuid, List<Account> chunkAccounts) throws AccountDatabaseCrawlerRestartException {
|
public void timeAndProcessCrawlChunk(Optional<UUID> fromUuid, List<Account> chunkAccounts) {
|
||||||
try (Timer.Context timer = processChunkTimer.time()) {
|
try (Timer.Context timer = processChunkTimer.time()) {
|
||||||
onCrawlChunk(fromUuid, chunkAccounts);
|
onCrawlChunk(fromUuid, chunkAccounts);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2013-2020 Signal Messenger, LLC
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
package org.whispersystems.textsecuregcm.storage;
|
|
||||||
|
|
||||||
public class AccountDatabaseCrawlerRestartException extends Exception {
|
|
||||||
public AccountDatabaseCrawlerRestartException(String s) {
|
|
||||||
super(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AccountDatabaseCrawlerRestartException(Exception e) {
|
|
||||||
super(e);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -123,8 +123,7 @@ public class CrawlAccountsCommand extends EnvironmentCommand<WhisperServerConfig
|
||||||
yield new AccountDatabaseCrawler("General-purpose account crawler",
|
yield new AccountDatabaseCrawler("General-purpose account crawler",
|
||||||
accountsManager,
|
accountsManager,
|
||||||
accountDatabaseCrawlerCache, accountDatabaseCrawlerListeners,
|
accountDatabaseCrawlerCache, accountDatabaseCrawlerListeners,
|
||||||
configuration.getAccountDatabaseCrawlerConfiguration().getChunkSize(),
|
configuration.getAccountDatabaseCrawlerConfiguration().getChunkSize()
|
||||||
dynamicConfigurationManager
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case ACCOUNT_CLEANER -> {
|
case ACCOUNT_CLEANER -> {
|
||||||
|
@ -138,8 +137,7 @@ public class CrawlAccountsCommand extends EnvironmentCommand<WhisperServerConfig
|
||||||
accountsManager,
|
accountsManager,
|
||||||
accountDatabaseCrawlerCache,
|
accountDatabaseCrawlerCache,
|
||||||
List.of(new AccountCleaner(accountsManager, accountDeletionExecutor)),
|
List.of(new AccountCleaner(accountsManager, accountDeletionExecutor)),
|
||||||
configuration.getAccountDatabaseCrawlerConfiguration().getChunkSize(),
|
configuration.getAccountDatabaseCrawlerConfiguration().getChunkSize()
|
||||||
dynamicConfigurationManager
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -77,7 +77,7 @@ class AccountCleanerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testAccounts() throws AccountDatabaseCrawlerRestartException, InterruptedException {
|
void testAccounts() throws InterruptedException {
|
||||||
AccountCleaner accountCleaner = new AccountCleaner(accountsManager, deletionExecutor);
|
AccountCleaner accountCleaner = new AccountCleaner(accountsManager, deletionExecutor);
|
||||||
accountCleaner.onCrawlStart();
|
accountCleaner.onCrawlStart();
|
||||||
accountCleaner.timeAndProcessCrawlChunk(Optional.empty(),
|
accountCleaner.timeAndProcessCrawlChunk(Optional.empty(),
|
||||||
|
|
|
@ -7,9 +7,7 @@ package org.whispersystems.textsecuregcm.storage;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.doThrow;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ -20,8 +18,6 @@ import java.util.UUID;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicAccountDatabaseCrawlerConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
|
import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
|
||||||
|
|
||||||
class AccountDatabaseCrawlerIntegrationTest {
|
class AccountDatabaseCrawlerIntegrationTest {
|
||||||
|
@ -60,54 +56,10 @@ class AccountDatabaseCrawlerIntegrationTest {
|
||||||
.thenReturn(new AccountCrawlChunk(List.of(secondAccount), SECOND_UUID))
|
.thenReturn(new AccountCrawlChunk(List.of(secondAccount), SECOND_UUID))
|
||||||
.thenReturn(new AccountCrawlChunk(Collections.emptyList(), null));
|
.thenReturn(new AccountCrawlChunk(Collections.emptyList(), null));
|
||||||
|
|
||||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = mock(
|
|
||||||
DynamicConfigurationManager.class);
|
|
||||||
final DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
|
|
||||||
DynamicAccountDatabaseCrawlerConfiguration accountDatabaseCrawlerConfiguration = new DynamicAccountDatabaseCrawlerConfiguration(
|
|
||||||
true, true);
|
|
||||||
when(dynamicConfiguration.getAccountDatabaseCrawlerConfiguration()).thenReturn(accountDatabaseCrawlerConfiguration);
|
|
||||||
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
|
|
||||||
|
|
||||||
final AccountDatabaseCrawlerCache crawlerCache = new AccountDatabaseCrawlerCache(
|
final AccountDatabaseCrawlerCache crawlerCache = new AccountDatabaseCrawlerCache(
|
||||||
REDIS_CLUSTER_EXTENSION.getRedisCluster(), "test");
|
REDIS_CLUSTER_EXTENSION.getRedisCluster(), "test");
|
||||||
accountDatabaseCrawler = new AccountDatabaseCrawler("test", accountsManager, crawlerCache, List.of(listener),
|
accountDatabaseCrawler = new AccountDatabaseCrawler("test", accountsManager, crawlerCache, List.of(listener),
|
||||||
CHUNK_SIZE, dynamicConfigurationManager);
|
CHUNK_SIZE);
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testCrawlUninterrupted() throws AccountDatabaseCrawlerRestartException {
|
|
||||||
accountDatabaseCrawler.doPeriodicWork();
|
|
||||||
accountDatabaseCrawler.doPeriodicWork();
|
|
||||||
accountDatabaseCrawler.doPeriodicWork();
|
|
||||||
|
|
||||||
verify(accountsManager).getAllFromDynamo(CHUNK_SIZE);
|
|
||||||
verify(accountsManager).getAllFromDynamo(FIRST_UUID, CHUNK_SIZE);
|
|
||||||
verify(accountsManager).getAllFromDynamo(SECOND_UUID, CHUNK_SIZE);
|
|
||||||
|
|
||||||
verify(listener).onCrawlStart();
|
|
||||||
verify(listener).timeAndProcessCrawlChunk(Optional.empty(), List.of(firstAccount));
|
|
||||||
verify(listener).timeAndProcessCrawlChunk(Optional.of(FIRST_UUID), List.of(secondAccount));
|
|
||||||
verify(listener).onCrawlEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testCrawlWithReset() throws AccountDatabaseCrawlerRestartException {
|
|
||||||
doThrow(new AccountDatabaseCrawlerRestartException("OH NO")).doNothing()
|
|
||||||
.when(listener).timeAndProcessCrawlChunk(Optional.empty(), List.of(firstAccount));
|
|
||||||
|
|
||||||
accountDatabaseCrawler.doPeriodicWork();
|
|
||||||
accountDatabaseCrawler.doPeriodicWork();
|
|
||||||
accountDatabaseCrawler.doPeriodicWork();
|
|
||||||
accountDatabaseCrawler.doPeriodicWork();
|
|
||||||
|
|
||||||
verify(accountsManager, times(2)).getAllFromDynamo(CHUNK_SIZE);
|
|
||||||
verify(accountsManager).getAllFromDynamo(FIRST_UUID, CHUNK_SIZE);
|
|
||||||
verify(accountsManager).getAllFromDynamo(SECOND_UUID, CHUNK_SIZE);
|
|
||||||
|
|
||||||
verify(listener, times(2)).onCrawlStart();
|
|
||||||
verify(listener, times(2)).timeAndProcessCrawlChunk(Optional.empty(), List.of(firstAccount));
|
|
||||||
verify(listener).timeAndProcessCrawlChunk(Optional.of(FIRST_UUID), List.of(secondAccount));
|
|
||||||
verify(listener).onCrawlEnd();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -9,11 +9,9 @@ import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.doThrow;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoInteractions;
|
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ -23,16 +21,12 @@ import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicAccountDatabaseCrawlerConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountCrawlChunk;
|
import org.whispersystems.textsecuregcm.storage.AccountCrawlChunk;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawler;
|
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawler;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerCache;
|
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerCache;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerListener;
|
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerListener;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerRestartException;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
|
||||||
|
|
||||||
class AccountDatabaseCrawlerTest {
|
class AccountDatabaseCrawlerTest {
|
||||||
|
|
||||||
|
@ -48,11 +42,8 @@ class AccountDatabaseCrawlerTest {
|
||||||
private final AccountDatabaseCrawlerListener listener = mock(AccountDatabaseCrawlerListener.class);
|
private final AccountDatabaseCrawlerListener listener = mock(AccountDatabaseCrawlerListener.class);
|
||||||
private final AccountDatabaseCrawlerCache cache = mock(AccountDatabaseCrawlerCache.class);
|
private final AccountDatabaseCrawlerCache cache = mock(AccountDatabaseCrawlerCache.class);
|
||||||
|
|
||||||
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = mock(
|
|
||||||
DynamicConfigurationManager.class);
|
|
||||||
|
|
||||||
private final AccountDatabaseCrawler crawler =
|
private final AccountDatabaseCrawler crawler =
|
||||||
new AccountDatabaseCrawler("test", accounts, cache, List.of(listener), CHUNK_SIZE, dynamicConfigurationManager);
|
new AccountDatabaseCrawler("test", accounts, cache, List.of(listener), CHUNK_SIZE);
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
|
@ -67,132 +58,10 @@ class AccountDatabaseCrawlerTest {
|
||||||
new AccountCrawlChunk(Collections.emptyList(), null));
|
new AccountCrawlChunk(Collections.emptyList(), null));
|
||||||
|
|
||||||
when(cache.claimActiveWork(any(), anyLong())).thenReturn(true);
|
when(cache.claimActiveWork(any(), anyLong())).thenReturn(true);
|
||||||
|
|
||||||
final DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
|
|
||||||
final DynamicAccountDatabaseCrawlerConfiguration accountDatabaseCrawlerConfiguration =
|
|
||||||
new DynamicAccountDatabaseCrawlerConfiguration(true, true);
|
|
||||||
when(dynamicConfiguration.getAccountDatabaseCrawlerConfiguration()).thenReturn(accountDatabaseCrawlerConfiguration);
|
|
||||||
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testCrawlStart() throws AccountDatabaseCrawlerRestartException {
|
void testCrawlAllAccounts() {
|
||||||
when(cache.getLastUuid()).thenReturn(Optional.empty());
|
|
||||||
|
|
||||||
crawler.doPeriodicWork();
|
|
||||||
|
|
||||||
verify(cache, times(1)).claimActiveWork(any(String.class), anyLong());
|
|
||||||
verify(cache, times(1)).getLastUuid();
|
|
||||||
verify(listener, times(1)).onCrawlStart();
|
|
||||||
verify(accounts, times(1)).getAllFromDynamo(eq(CHUNK_SIZE));
|
|
||||||
verify(accounts, times(0)).getAllFromDynamo(any(UUID.class), eq(CHUNK_SIZE));
|
|
||||||
verify(account1, times(0)).getUuid();
|
|
||||||
verify(listener, times(1)).timeAndProcessCrawlChunk(eq(Optional.empty()), eq(List.of(account1, account2)));
|
|
||||||
verify(cache, times(1)).setLastUuid(eq(Optional.of(ACCOUNT2)));
|
|
||||||
verify(cache, times(1)).releaseActiveWork(any(String.class));
|
|
||||||
|
|
||||||
verifyNoMoreInteractions(account1);
|
|
||||||
verifyNoMoreInteractions(account2);
|
|
||||||
verifyNoMoreInteractions(accounts);
|
|
||||||
verifyNoMoreInteractions(listener);
|
|
||||||
verifyNoMoreInteractions(cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testCrawlChunk() throws AccountDatabaseCrawlerRestartException {
|
|
||||||
when(cache.getLastUuid()).thenReturn(Optional.of(ACCOUNT1));
|
|
||||||
|
|
||||||
crawler.doPeriodicWork();
|
|
||||||
|
|
||||||
verify(cache, times(1)).claimActiveWork(any(String.class), anyLong());
|
|
||||||
verify(cache, times(1)).getLastUuid();
|
|
||||||
verify(accounts, times(0)).getAllFromDynamo(eq(CHUNK_SIZE));
|
|
||||||
verify(accounts, times(1)).getAllFromDynamo(eq(ACCOUNT1), eq(CHUNK_SIZE));
|
|
||||||
verify(listener, times(1)).timeAndProcessCrawlChunk(eq(Optional.of(ACCOUNT1)), eq(List.of(account2)));
|
|
||||||
verify(cache, times(1)).setLastUuid(eq(Optional.of(ACCOUNT2)));
|
|
||||||
verify(cache, times(1)).releaseActiveWork(any(String.class));
|
|
||||||
|
|
||||||
verifyNoInteractions(account1);
|
|
||||||
|
|
||||||
verifyNoMoreInteractions(account2);
|
|
||||||
verifyNoMoreInteractions(accounts);
|
|
||||||
verifyNoMoreInteractions(listener);
|
|
||||||
verifyNoMoreInteractions(cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testCrawlChunkAccelerated() throws AccountDatabaseCrawlerRestartException {
|
|
||||||
when(cache.getLastUuid()).thenReturn(Optional.of(ACCOUNT1));
|
|
||||||
|
|
||||||
crawler.doPeriodicWork();
|
|
||||||
|
|
||||||
verify(cache, times(1)).claimActiveWork(any(String.class), anyLong());
|
|
||||||
verify(cache, times(1)).getLastUuid();
|
|
||||||
verify(accounts, times(0)).getAllFromDynamo(eq(CHUNK_SIZE));
|
|
||||||
verify(accounts, times(1)).getAllFromDynamo(eq(ACCOUNT1), eq(CHUNK_SIZE));
|
|
||||||
verify(listener, times(1)).timeAndProcessCrawlChunk(eq(Optional.of(ACCOUNT1)), eq(List.of(account2)));
|
|
||||||
verify(cache, times(1)).setLastUuid(eq(Optional.of(ACCOUNT2)));
|
|
||||||
verify(cache, times(1)).releaseActiveWork(any(String.class));
|
|
||||||
|
|
||||||
verifyNoInteractions(account1);
|
|
||||||
|
|
||||||
verifyNoMoreInteractions(account2);
|
|
||||||
verifyNoMoreInteractions(accounts);
|
|
||||||
verifyNoMoreInteractions(listener);
|
|
||||||
verifyNoMoreInteractions(cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testCrawlChunkRestart() throws AccountDatabaseCrawlerRestartException {
|
|
||||||
when(cache.getLastUuid()).thenReturn(Optional.of(ACCOUNT1));
|
|
||||||
doThrow(AccountDatabaseCrawlerRestartException.class).when(listener)
|
|
||||||
.timeAndProcessCrawlChunk(eq(Optional.of(ACCOUNT1)), eq(List.of(account2)));
|
|
||||||
|
|
||||||
crawler.doPeriodicWork();
|
|
||||||
|
|
||||||
verify(cache, times(1)).claimActiveWork(any(String.class), anyLong());
|
|
||||||
verify(cache, times(1)).getLastUuid();
|
|
||||||
verify(accounts, times(0)).getAllFromDynamo(eq(CHUNK_SIZE));
|
|
||||||
verify(accounts, times(1)).getAllFromDynamo(eq(ACCOUNT1), eq(CHUNK_SIZE));
|
|
||||||
verify(account2, times(0)).getNumber();
|
|
||||||
verify(listener, times(1)).timeAndProcessCrawlChunk(eq(Optional.of(ACCOUNT1)), eq(List.of(account2)));
|
|
||||||
verify(cache, times(1)).setLastUuid(eq(Optional.empty()));
|
|
||||||
verify(cache, times(1)).releaseActiveWork(any(String.class));
|
|
||||||
|
|
||||||
verifyNoInteractions(account1);
|
|
||||||
|
|
||||||
verifyNoMoreInteractions(account2);
|
|
||||||
verifyNoMoreInteractions(accounts);
|
|
||||||
verifyNoMoreInteractions(listener);
|
|
||||||
verifyNoMoreInteractions(cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testCrawlEnd() {
|
|
||||||
when(cache.getLastUuid()).thenReturn(Optional.of(ACCOUNT2));
|
|
||||||
|
|
||||||
crawler.doPeriodicWork();
|
|
||||||
|
|
||||||
verify(cache, times(1)).claimActiveWork(any(String.class), anyLong());
|
|
||||||
verify(cache, times(1)).getLastUuid();
|
|
||||||
verify(accounts, times(0)).getAllFromDynamo(eq(CHUNK_SIZE));
|
|
||||||
verify(accounts, times(1)).getAllFromDynamo(eq(ACCOUNT2), eq(CHUNK_SIZE));
|
|
||||||
verify(account1, times(0)).getNumber();
|
|
||||||
verify(account2, times(0)).getNumber();
|
|
||||||
verify(listener, times(1)).onCrawlEnd();
|
|
||||||
verify(cache, times(1)).setLastUuid(eq(Optional.empty()));
|
|
||||||
verify(cache, times(1)).releaseActiveWork(any(String.class));
|
|
||||||
|
|
||||||
verifyNoInteractions(account1);
|
|
||||||
verifyNoInteractions(account2);
|
|
||||||
|
|
||||||
verifyNoMoreInteractions(accounts);
|
|
||||||
verifyNoMoreInteractions(listener);
|
|
||||||
verifyNoMoreInteractions(cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testCrawlAllAccounts() throws Exception {
|
|
||||||
when(cache.getLastUuid())
|
when(cache.getLastUuid())
|
||||||
.thenReturn(Optional.empty());
|
.thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ import java.util.concurrent.TimeUnit;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerRestartException;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.Device;
|
import org.whispersystems.textsecuregcm.storage.Device;
|
||||||
import org.whispersystems.textsecuregcm.storage.PushFeedbackProcessor;
|
import org.whispersystems.textsecuregcm.storage.PushFeedbackProcessor;
|
||||||
|
@ -36,7 +35,7 @@ import org.whispersystems.textsecuregcm.util.Util;
|
||||||
|
|
||||||
class PushFeedbackProcessorTest {
|
class PushFeedbackProcessorTest {
|
||||||
|
|
||||||
private AccountsManager accountsManager = mock(AccountsManager.class);
|
private final AccountsManager accountsManager = mock(AccountsManager.class);
|
||||||
|
|
||||||
private Account uninstalledAccount = mock(Account.class);
|
private Account uninstalledAccount = mock(Account.class);
|
||||||
private Account mixedAccount = mock(Account.class);
|
private Account mixedAccount = mock(Account.class);
|
||||||
|
@ -99,9 +98,8 @@ class PushFeedbackProcessorTest {
|
||||||
Set.of(uninstalledAccount, mixedAccount, freshAccount, cleanAccount, stillActiveAccount));
|
Set.of(uninstalledAccount, mixedAccount, freshAccount, cleanAccount, stillActiveAccount));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testEmpty() throws AccountDatabaseCrawlerRestartException {
|
void testEmpty() {
|
||||||
PushFeedbackProcessor processor = new PushFeedbackProcessor(accountsManager, Executors.newSingleThreadExecutor());
|
PushFeedbackProcessor processor = new PushFeedbackProcessor(accountsManager, Executors.newSingleThreadExecutor());
|
||||||
processor.timeAndProcessCrawlChunk(Optional.of(UUID.randomUUID()), Collections.emptyList());
|
processor.timeAndProcessCrawlChunk(Optional.of(UUID.randomUUID()), Collections.emptyList());
|
||||||
|
|
||||||
|
@ -109,7 +107,7 @@ class PushFeedbackProcessorTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUpdate() throws AccountDatabaseCrawlerRestartException {
|
void testUpdate() {
|
||||||
PushFeedbackProcessor processor = new PushFeedbackProcessor(accountsManager, Executors.newSingleThreadExecutor());
|
PushFeedbackProcessor processor = new PushFeedbackProcessor(accountsManager, Executors.newSingleThreadExecutor());
|
||||||
processor.timeAndProcessCrawlChunk(Optional.of(UUID.randomUUID()),
|
processor.timeAndProcessCrawlChunk(Optional.of(UUID.randomUUID()),
|
||||||
List.of(uninstalledAccount, mixedAccount, stillActiveAccount, freshAccount, cleanAccount));
|
List.of(uninstalledAccount, mixedAccount, stillActiveAccount, freshAccount, cleanAccount));
|
||||||
|
|
Loading…
Reference in New Issue