Switch to Redis for all caching.

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2015-01-29 15:37:28 -08:00
parent 1f5ee36a6b
commit 75aec0a8d4
15 changed files with 118 additions and 264 deletions

View File

@ -86,11 +86,6 @@
<artifactId>gcm-server</artifactId> <artifactId>gcm-server</artifactId>
<version>1.0.2</version> <version>1.0.2</version>
</dependency> </dependency>
<dependency>
<groupId>net.spy</groupId>
<artifactId>spymemcached</artifactId>
<version>2.10.1</version>
</dependency>
<dependency> <dependency>
<groupId>com.notnoop.apns</groupId> <groupId>com.notnoop.apns</groupId>
<artifactId>apns</artifactId> <artifactId>apns</artifactId>

View File

@ -17,11 +17,10 @@
package org.whispersystems.textsecuregcm; package org.whispersystems.textsecuregcm;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.textsecuregcm.configuration.DirectoryConfiguration; import org.whispersystems.textsecuregcm.configuration.RedisConfiguration;
import org.whispersystems.textsecuregcm.configuration.FederationConfiguration; import org.whispersystems.textsecuregcm.configuration.FederationConfiguration;
import org.whispersystems.textsecuregcm.configuration.GraphiteConfiguration; import org.whispersystems.textsecuregcm.configuration.GraphiteConfiguration;
import org.whispersystems.textsecuregcm.configuration.MemcacheConfiguration; import org.whispersystems.textsecuregcm.configuration.MemcacheConfiguration;
import org.whispersystems.textsecuregcm.configuration.MessageStoreConfiguration;
import org.whispersystems.textsecuregcm.configuration.NexmoConfiguration; import org.whispersystems.textsecuregcm.configuration.NexmoConfiguration;
import org.whispersystems.textsecuregcm.configuration.PushConfiguration; import org.whispersystems.textsecuregcm.configuration.PushConfiguration;
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration; import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration;
@ -60,12 +59,12 @@ public class WhisperServerConfiguration extends Configuration {
@NotNull @NotNull
@Valid @Valid
@JsonProperty @JsonProperty
private MemcacheConfiguration memcache; private RedisConfiguration cache;
@NotNull @NotNull
@Valid @Valid
@JsonProperty @JsonProperty
private DirectoryConfiguration directory; private RedisConfiguration directory;
@Valid @Valid
@NotNull @NotNull
@ -128,11 +127,11 @@ public class WhisperServerConfiguration extends Configuration {
return s3; return s3;
} }
public MemcacheConfiguration getMemcacheConfiguration() { public RedisConfiguration getCacheConfiguration() {
return memcache; return cache;
} }
public DirectoryConfiguration getDirectoryConfiguration() { public RedisConfiguration getDirectoryConfiguration() {
return directory; return directory;
} }

View File

@ -21,7 +21,6 @@ import com.codahale.metrics.graphite.GraphiteReporter;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.Client;
import net.spy.memcached.MemcachedClient;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.eclipse.jetty.servlets.CrossOriginFilter; import org.eclipse.jetty.servlets.CrossOriginFilter;
import org.skife.jdbi.v2.DBI; import org.skife.jdbi.v2.DBI;
@ -44,14 +43,13 @@ import org.whispersystems.textsecuregcm.controllers.ReceiptController;
import org.whispersystems.textsecuregcm.federation.FederatedClientManager; import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
import org.whispersystems.textsecuregcm.federation.FederatedPeer; import org.whispersystems.textsecuregcm.federation.FederatedPeer;
import org.whispersystems.textsecuregcm.limits.RateLimiters; import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.liquibase.NameableMigrationsBundle;
import org.whispersystems.textsecuregcm.mappers.IOExceptionMapper; import org.whispersystems.textsecuregcm.mappers.IOExceptionMapper;
import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper; import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper;
import org.whispersystems.textsecuregcm.metrics.CpuUsageGauge; import org.whispersystems.textsecuregcm.metrics.CpuUsageGauge;
import org.whispersystems.textsecuregcm.metrics.FreeMemoryGauge; import org.whispersystems.textsecuregcm.metrics.FreeMemoryGauge;
import org.whispersystems.textsecuregcm.metrics.NetworkReceivedGauge; import org.whispersystems.textsecuregcm.metrics.NetworkReceivedGauge;
import org.whispersystems.textsecuregcm.metrics.NetworkSentGauge; import org.whispersystems.textsecuregcm.metrics.NetworkSentGauge;
import org.whispersystems.textsecuregcm.providers.MemcacheHealthCheck;
import org.whispersystems.textsecuregcm.providers.MemcachedClientFactory;
import org.whispersystems.textsecuregcm.providers.RedisClientFactory; import org.whispersystems.textsecuregcm.providers.RedisClientFactory;
import org.whispersystems.textsecuregcm.providers.RedisHealthCheck; import org.whispersystems.textsecuregcm.providers.RedisHealthCheck;
import org.whispersystems.textsecuregcm.providers.TimeProvider; import org.whispersystems.textsecuregcm.providers.TimeProvider;
@ -80,7 +78,6 @@ import org.whispersystems.textsecuregcm.websocket.AuthenticatedConnectListener;
import org.whispersystems.textsecuregcm.websocket.ProvisioningConnectListener; import org.whispersystems.textsecuregcm.websocket.ProvisioningConnectListener;
import org.whispersystems.textsecuregcm.websocket.WebSocketAccountAuthenticator; import org.whispersystems.textsecuregcm.websocket.WebSocketAccountAuthenticator;
import org.whispersystems.textsecuregcm.workers.DirectoryCommand; import org.whispersystems.textsecuregcm.workers.DirectoryCommand;
import org.whispersystems.textsecuregcm.liquibase.NameableMigrationsBundle;
import org.whispersystems.textsecuregcm.workers.VacuumCommand; import org.whispersystems.textsecuregcm.workers.VacuumCommand;
import org.whispersystems.websocket.WebSocketResourceProviderFactory; import org.whispersystems.websocket.WebSocketResourceProviderFactory;
import org.whispersystems.websocket.setup.WebSocketEnvironment; import org.whispersystems.websocket.setup.WebSocketEnvironment;
@ -149,22 +146,22 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
Keys keys = database.onDemand(Keys.class); Keys keys = database.onDemand(Keys.class);
Messages messages = messagedb.onDemand(Messages.class); Messages messages = messagedb.onDemand(Messages.class);
MemcachedClient memcachedClient = new MemcachedClientFactory(config.getMemcacheConfiguration()).getClient(); JedisPool cacheClient = new RedisClientFactory(config.getCacheConfiguration().getUrl()).getRedisClientPool();
JedisPool directoryClient = new RedisClientFactory(config.getDirectoryConfiguration().getUrl()).getRedisClientPool(); JedisPool directoryClient = new RedisClientFactory(config.getDirectoryConfiguration().getUrl()).getRedisClientPool();
Client httpClient = new JerseyClientBuilder(environment).using(config.getJerseyClientConfiguration()) Client httpClient = new JerseyClientBuilder(environment).using(config.getJerseyClientConfiguration())
.build(getName()); .build(getName());
DirectoryManager directory = new DirectoryManager(directoryClient); DirectoryManager directory = new DirectoryManager(directoryClient);
PendingAccountsManager pendingAccountsManager = new PendingAccountsManager(pendingAccounts, memcachedClient); PendingAccountsManager pendingAccountsManager = new PendingAccountsManager(pendingAccounts, cacheClient);
PendingDevicesManager pendingDevicesManager = new PendingDevicesManager (pendingDevices, memcachedClient ); PendingDevicesManager pendingDevicesManager = new PendingDevicesManager (pendingDevices, cacheClient);
AccountsManager accountsManager = new AccountsManager(accounts, directory, memcachedClient); AccountsManager accountsManager = new AccountsManager(accounts, directory, cacheClient);
FederatedClientManager federatedClientManager = new FederatedClientManager(config.getFederationConfiguration()); FederatedClientManager federatedClientManager = new FederatedClientManager(config.getFederationConfiguration());
MessagesManager messagesManager = new MessagesManager(messages); MessagesManager messagesManager = new MessagesManager(messages);
PubSubManager pubSubManager = new PubSubManager(directoryClient); PubSubManager pubSubManager = new PubSubManager(cacheClient);
PushServiceClient pushServiceClient = new PushServiceClient(httpClient, config.getPushConfiguration()); PushServiceClient pushServiceClient = new PushServiceClient(httpClient, config.getPushConfiguration());
WebsocketSender websocketSender = new WebsocketSender(messagesManager, pubSubManager); WebsocketSender websocketSender = new WebsocketSender(messagesManager, pubSubManager);
AccountAuthenticator deviceAuthenticator = new AccountAuthenticator(accountsManager); AccountAuthenticator deviceAuthenticator = new AccountAuthenticator(accountsManager);
RateLimiters rateLimiters = new RateLimiters(config.getLimitsConfiguration(), memcachedClient); RateLimiters rateLimiters = new RateLimiters(config.getLimitsConfiguration(), cacheClient);
TwilioSmsSender twilioSmsSender = new TwilioSmsSender(config.getTwilioConfiguration()); TwilioSmsSender twilioSmsSender = new TwilioSmsSender(config.getTwilioConfiguration());
Optional<NexmoSmsSender> nexmoSmsSender = initializeNexmoSmsSender(config.getNexmoConfiguration()); Optional<NexmoSmsSender> nexmoSmsSender = initializeNexmoSmsSender(config.getNexmoConfiguration());
@ -233,7 +230,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
} }
environment.healthChecks().register("directory", new RedisHealthCheck(directoryClient)); environment.healthChecks().register("directory", new RedisHealthCheck(directoryClient));
environment.healthChecks().register("memcache", new MemcacheHealthCheck(memcachedClient)); environment.healthChecks().register("cache", new RedisHealthCheck(cacheClient));
environment.jersey().register(new IOExceptionMapper()); environment.jersey().register(new IOExceptionMapper());
environment.jersey().register(new RateLimitExceededExceptionMapper()); environment.jersey().register(new RateLimitExceededExceptionMapper());

View File

@ -1,46 +0,0 @@
/**
* Copyright (C) 2013 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.textsecuregcm.configuration;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.NotEmpty;
public class MemcacheConfiguration {
@NotEmpty
@JsonProperty
private String servers;
@JsonProperty
private String user;
@JsonProperty
private String password;
public String getServers() {
return servers;
}
public String getUser() {
return user;
}
public String getPassword() {
return password;
}
}

View File

@ -21,7 +21,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.URL; import org.hibernate.validator.constraints.URL;
public class DirectoryConfiguration { public class RedisConfiguration {
@JsonProperty @JsonProperty
@NotEmpty @NotEmpty

View File

@ -16,14 +16,20 @@
*/ */
package org.whispersystems.textsecuregcm.limits; package org.whispersystems.textsecuregcm.limits;
import java.io.Serializable; import com.fasterxml.jackson.annotation.JsonProperty;
public class LeakyBucket implements Serializable { public class LeakyBucket {
@JsonProperty
private final int bucketSize; private final int bucketSize;
@JsonProperty
private final double leakRatePerMillis; private final double leakRatePerMillis;
@JsonProperty
private int spaceRemaining; private int spaceRemaining;
@JsonProperty
private long lastUpdateTimeMillis; private long lastUpdateTimeMillis;
public LeakyBucket(int bucketSize, double leakRatePerMillis) { public LeakyBucket(int bucketSize, double leakRatePerMillis) {

View File

@ -19,27 +19,38 @@ package org.whispersystems.textsecuregcm.limits;
import com.codahale.metrics.Meter; import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries; import com.codahale.metrics.SharedMetricRegistries;
import net.spy.memcached.MemcachedClient; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException; import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
import org.whispersystems.textsecuregcm.util.Constants; import org.whispersystems.textsecuregcm.util.Constants;
import org.whispersystems.textsecuregcm.util.SystemMapper;
import java.io.IOException;
import static com.codahale.metrics.MetricRegistry.name; import static com.codahale.metrics.MetricRegistry.name;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class RateLimiter { public class RateLimiter {
private final Logger logger = LoggerFactory.getLogger(RateLimiter.class);
private final ObjectMapper mapper = SystemMapper.getMapper();
private final Meter meter; private final Meter meter;
private final MemcachedClient memcachedClient; private final JedisPool cacheClient;
private final String name; private final String name;
private final int bucketSize; private final int bucketSize;
private final double leakRatePerMillis; private final double leakRatePerMillis;
public RateLimiter(MemcachedClient memcachedClient, String name, public RateLimiter(JedisPool cacheClient, String name,
int bucketSize, double leakRatePerMinute) int bucketSize, double leakRatePerMinute)
{ {
MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME); MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
this.meter = metricRegistry.meter(name(getClass(), name, "exceeded")); this.meter = metricRegistry.meter(name(getClass(), name, "exceeded"));
this.memcachedClient = memcachedClient; this.cacheClient = cacheClient;
this.name = name; this.name = name;
this.bucketSize = bucketSize; this.bucketSize = bucketSize;
this.leakRatePerMillis = leakRatePerMinute / (60.0 * 1000.0); this.leakRatePerMillis = leakRatePerMinute / (60.0 * 1000.0);
@ -61,21 +72,29 @@ public class RateLimiter {
} }
private void setBucket(String key, LeakyBucket bucket) { private void setBucket(String key, LeakyBucket bucket) {
memcachedClient.set(getBucketName(key), try (Jedis jedis = cacheClient.getResource()) {
(int)Math.ceil((bucketSize / leakRatePerMillis) / 1000), bucket); String serialized = mapper.writeValueAsString(bucket);
jedis.setex(getBucketName(key), (int) Math.ceil((bucketSize / leakRatePerMillis) / 1000), serialized);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
}
} }
private LeakyBucket getBucket(String key) { private LeakyBucket getBucket(String key) {
LeakyBucket bucket = (LeakyBucket)memcachedClient.get(getBucketName(key)); try (Jedis jedis = cacheClient.getResource()) {
String serialized = jedis.get(getBucketName(key));
if (bucket == null) { if (serialized != null) {
return new LeakyBucket(bucketSize, leakRatePerMillis); return mapper.readValue(serialized, LeakyBucket.class);
} else {
return bucket;
} }
} catch (IOException e) {
logger.warn("Deserialization error", e);
}
return new LeakyBucket(bucketSize, leakRatePerMillis);
} }
private String getBucketName(String key) { private String getBucketName(String key) {
return LeakyBucket.class.getSimpleName() + name + key; return "leaky_bucket::" + name + "::" + key;
} }
} }

View File

@ -17,9 +17,10 @@
package org.whispersystems.textsecuregcm.limits; package org.whispersystems.textsecuregcm.limits;
import net.spy.memcached.MemcachedClient;
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration; import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration;
import redis.clients.jedis.JedisPool;
public class RateLimiters { public class RateLimiters {
private final RateLimiter smsDestinationLimiter; private final RateLimiter smsDestinationLimiter;
@ -34,40 +35,40 @@ public class RateLimiters {
private final RateLimiter allocateDeviceLimiter; private final RateLimiter allocateDeviceLimiter;
private final RateLimiter verifyDeviceLimiter; private final RateLimiter verifyDeviceLimiter;
public RateLimiters(RateLimitsConfiguration config, MemcachedClient memcachedClient) { public RateLimiters(RateLimitsConfiguration config, JedisPool cacheClient) {
this.smsDestinationLimiter = new RateLimiter(memcachedClient, "smsDestination", this.smsDestinationLimiter = new RateLimiter(cacheClient, "smsDestination",
config.getSmsDestination().getBucketSize(), config.getSmsDestination().getBucketSize(),
config.getSmsDestination().getLeakRatePerMinute()); config.getSmsDestination().getLeakRatePerMinute());
this.voiceDestinationLimiter = new RateLimiter(memcachedClient, "voxDestination", this.voiceDestinationLimiter = new RateLimiter(cacheClient, "voxDestination",
config.getVoiceDestination().getBucketSize(), config.getVoiceDestination().getBucketSize(),
config.getVoiceDestination().getLeakRatePerMinute()); config.getVoiceDestination().getLeakRatePerMinute());
this.verifyLimiter = new RateLimiter(memcachedClient, "verify", this.verifyLimiter = new RateLimiter(cacheClient, "verify",
config.getVerifyNumber().getBucketSize(), config.getVerifyNumber().getBucketSize(),
config.getVerifyNumber().getLeakRatePerMinute()); config.getVerifyNumber().getLeakRatePerMinute());
this.attachmentLimiter = new RateLimiter(memcachedClient, "attachmentCreate", this.attachmentLimiter = new RateLimiter(cacheClient, "attachmentCreate",
config.getAttachments().getBucketSize(), config.getAttachments().getBucketSize(),
config.getAttachments().getLeakRatePerMinute()); config.getAttachments().getLeakRatePerMinute());
this.contactsLimiter = new RateLimiter(memcachedClient, "contactsQuery", this.contactsLimiter = new RateLimiter(cacheClient, "contactsQuery",
config.getContactQueries().getBucketSize(), config.getContactQueries().getBucketSize(),
config.getContactQueries().getLeakRatePerMinute()); config.getContactQueries().getLeakRatePerMinute());
this.preKeysLimiter = new RateLimiter(memcachedClient, "prekeys", this.preKeysLimiter = new RateLimiter(cacheClient, "prekeys",
config.getPreKeys().getBucketSize(), config.getPreKeys().getBucketSize(),
config.getPreKeys().getLeakRatePerMinute()); config.getPreKeys().getLeakRatePerMinute());
this.messagesLimiter = new RateLimiter(memcachedClient, "messages", this.messagesLimiter = new RateLimiter(cacheClient, "messages",
config.getMessages().getBucketSize(), config.getMessages().getBucketSize(),
config.getMessages().getLeakRatePerMinute()); config.getMessages().getLeakRatePerMinute());
this.allocateDeviceLimiter = new RateLimiter(memcachedClient, "allocateDevice", this.allocateDeviceLimiter = new RateLimiter(cacheClient, "allocateDevice",
config.getAllocateDevice().getBucketSize(), config.getAllocateDevice().getBucketSize(),
config.getAllocateDevice().getLeakRatePerMinute()); config.getAllocateDevice().getLeakRatePerMinute());
this.verifyDeviceLimiter = new RateLimiter(memcachedClient, "verifyDevice", this.verifyDeviceLimiter = new RateLimiter(cacheClient, "verifyDevice",
config.getVerifyDevice().getBucketSize(), config.getVerifyDevice().getBucketSize(),
config.getVerifyDevice().getLeakRatePerMinute()); config.getVerifyDevice().getLeakRatePerMinute());

View File

@ -1,53 +0,0 @@
/**
* Copyright (C) 2013 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.textsecuregcm.providers;
import com.codahale.metrics.health.HealthCheck;
import net.spy.memcached.MemcachedClient;
import java.security.SecureRandom;
public class MemcacheHealthCheck extends HealthCheck {
private final MemcachedClient client;
public MemcacheHealthCheck(MemcachedClient client) {
this.client = client;
}
@Override
protected Result check() throws Exception {
if (client == null) {
return Result.unhealthy("not configured");
}
int random = SecureRandom.getInstance("SHA1PRNG").nextInt();
int value = SecureRandom.getInstance("SHA1PRNG").nextInt();
this.client.set("HEALTH" + random, 2000, String.valueOf(value));
String result = (String)this.client.get("HEALTH" + random);
if (result == null || Integer.parseInt(result) != value) {
return Result.unhealthy("Fetch failed");
}
this.client.delete("HEALTH" + random);
return Result.healthy();
}
}

View File

@ -1,54 +0,0 @@
/**
* Copyright (C) 2013 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.textsecuregcm.providers;
import net.spy.memcached.AddrUtil;
import net.spy.memcached.ConnectionFactoryBuilder;
import net.spy.memcached.MemcachedClient;
import net.spy.memcached.auth.AuthDescriptor;
import net.spy.memcached.auth.PlainCallbackHandler;
import org.whispersystems.textsecuregcm.configuration.MemcacheConfiguration;
import org.whispersystems.textsecuregcm.util.Util;
import java.io.IOException;
public class MemcachedClientFactory {
private final MemcachedClient client;
public MemcachedClientFactory(MemcacheConfiguration config) throws IOException {
ConnectionFactoryBuilder builder = new ConnectionFactoryBuilder();
builder.setProtocol(ConnectionFactoryBuilder.Protocol.BINARY);
if (!Util.isEmpty(config.getUser())) {
AuthDescriptor ad = new AuthDescriptor(new String[] { "PLAIN" },
new PlainCallbackHandler(config.getUser(),
config.getPassword()));
builder.setAuthDescriptor(ad);
}
this.client = new MemcachedClient(builder.build(),
AddrUtil.getAddresses(config.getServers()));
}
public MemcachedClient getClient() {
return client;
}
}

View File

@ -16,7 +16,6 @@
*/ */
package org.whispersystems.textsecuregcm.providers; package org.whispersystems.textsecuregcm.providers;
import org.whispersystems.textsecuregcm.configuration.DirectoryConfiguration;
import org.whispersystems.textsecuregcm.util.Util; import org.whispersystems.textsecuregcm.util.Util;
import java.net.URI; import java.net.URI;

View File

@ -17,12 +17,9 @@
package org.whispersystems.textsecuregcm.storage; package org.whispersystems.textsecuregcm.storage;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import net.spy.memcached.MemcachedClient;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.entities.ClientContact; import org.whispersystems.textsecuregcm.entities.ClientContact;
@ -33,22 +30,25 @@ import java.io.IOException;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class AccountsManager { public class AccountsManager {
private final Logger logger = LoggerFactory.getLogger(AccountsManager.class); private final Logger logger = LoggerFactory.getLogger(AccountsManager.class);
private final Accounts accounts; private final Accounts accounts;
private final MemcachedClient memcachedClient; private final JedisPool cacheClient;
private final DirectoryManager directory; private final DirectoryManager directory;
private final ObjectMapper mapper; private final ObjectMapper mapper;
public AccountsManager(Accounts accounts, public AccountsManager(Accounts accounts,
DirectoryManager directory, DirectoryManager directory,
MemcachedClient memcachedClient) JedisPool cacheClient)
{ {
this.accounts = accounts; this.accounts = accounts;
this.directory = directory; this.directory = directory;
this.memcachedClient = memcachedClient; this.cacheClient = cacheClient;
this.mapper = SystemMapper.getMapper(); this.mapper = SystemMapper.getMapper();
} }
@ -112,25 +112,19 @@ public class AccountsManager {
} }
private void memcacheSet(String number, Account account) { private void memcacheSet(String number, Account account) {
if (memcachedClient != null) { try (Jedis jedis = cacheClient.getResource()) {
try { jedis.set(getKey(number), mapper.writeValueAsString(account));
String json = mapper.writeValueAsString(account);
memcachedClient.set(getKey(number), 0, json);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new IllegalArgumentException(e); throw new IllegalArgumentException(e);
} }
} }
}
private Optional<Account> memcacheGet(String number) { private Optional<Account> memcacheGet(String number) {
if (memcachedClient == null) return Optional.absent(); try (Jedis jedis = cacheClient.getResource()) {
String json = jedis.get(getKey(number));
try {
String json = (String)memcachedClient.get(getKey(number));
if (json != null) return Optional.of(mapper.readValue(json, Account.class)); if (json != null) return Optional.of(mapper.readValue(json, Account.class));
else return Optional.absent(); else return Optional.absent();
} catch (IOException e) { } catch (IOException e) {
logger.warn("AccountsManager", "Deserialization error", e); logger.warn("AccountsManager", "Deserialization error", e);
return Optional.absent(); return Optional.absent();

View File

@ -17,20 +17,21 @@
package org.whispersystems.textsecuregcm.storage; package org.whispersystems.textsecuregcm.storage;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import net.spy.memcached.MemcachedClient;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class PendingAccountsManager { public class PendingAccountsManager {
private static final String MEMCACHE_PREFIX = "pending_account"; private static final String CACHE_PREFIX = "pending_account::";
private final PendingAccounts pendingAccounts; private final PendingAccounts pendingAccounts;
private final MemcachedClient memcachedClient; private final JedisPool cacheClient;
public PendingAccountsManager(PendingAccounts pendingAccounts, public PendingAccountsManager(PendingAccounts pendingAccounts, JedisPool cacheClient)
MemcachedClient memcachedClient)
{ {
this.pendingAccounts = pendingAccounts; this.pendingAccounts = pendingAccounts;
this.memcachedClient = memcachedClient; this.cacheClient = cacheClient;
} }
public void store(String number, String code) { public void store(String number, String code) {
@ -58,22 +59,20 @@ public class PendingAccountsManager {
} }
private void memcacheSet(String number, String code) { private void memcacheSet(String number, String code) {
if (memcachedClient != null) { try (Jedis jedis = cacheClient.getResource()) {
memcachedClient.set(MEMCACHE_PREFIX + number, 0, code); jedis.set(CACHE_PREFIX + number, code);
} }
} }
private Optional<String> memcacheGet(String number) { private Optional<String> memcacheGet(String number) {
if (memcachedClient != null) { try (Jedis jedis = cacheClient.getResource()) {
return Optional.fromNullable((String)memcachedClient.get(MEMCACHE_PREFIX + number)); return Optional.fromNullable(jedis.get(CACHE_PREFIX + number));
} else {
return Optional.absent();
} }
} }
private void memcacheDelete(String number) { private void memcacheDelete(String number) {
if (memcachedClient != null) { try (Jedis jedis = cacheClient.getResource()) {
memcachedClient.delete(MEMCACHE_PREFIX + number); jedis.del(CACHE_PREFIX + number);
} }
} }
} }

View File

@ -17,20 +17,22 @@
package org.whispersystems.textsecuregcm.storage; package org.whispersystems.textsecuregcm.storage;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import net.spy.memcached.MemcachedClient;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class PendingDevicesManager { public class PendingDevicesManager {
private static final String MEMCACHE_PREFIX = "pending_devices"; private static final String CACHE_PREFIX = "pending_devices::";
private final PendingDevices pendingDevices; private final PendingDevices pendingDevices;
private final MemcachedClient memcachedClient; private final JedisPool cacheClient;
public PendingDevicesManager(PendingDevices pendingDevices, public PendingDevicesManager(PendingDevices pendingDevices,
MemcachedClient memcachedClient) JedisPool cacheClient)
{ {
this.pendingDevices = pendingDevices; this.pendingDevices = pendingDevices;
this.memcachedClient = memcachedClient; this.cacheClient = cacheClient;
} }
public void store(String number, String code) { public void store(String number, String code) {
@ -58,22 +60,20 @@ public class PendingDevicesManager {
} }
private void memcacheSet(String number, String code) { private void memcacheSet(String number, String code) {
if (memcachedClient != null) { try (Jedis jedis = cacheClient.getResource()) {
memcachedClient.set(MEMCACHE_PREFIX + number, 0, code); jedis.set(CACHE_PREFIX + number, code);
} }
} }
private Optional<String> memcacheGet(String number) { private Optional<String> memcacheGet(String number) {
if (memcachedClient != null) { try (Jedis jedis = cacheClient.getResource()) {
return Optional.fromNullable((String)memcachedClient.get(MEMCACHE_PREFIX + number)); return Optional.fromNullable(jedis.get(CACHE_PREFIX + number));
} else {
return Optional.absent();
} }
} }
private void memcacheDelete(String number) { private void memcacheDelete(String number) {
if (memcachedClient != null) { try (Jedis jedis = cacheClient.getResource()) {
memcachedClient.delete(MEMCACHE_PREFIX + number); jedis.del(CACHE_PREFIX + number);
} }
} }

View File

@ -17,13 +17,11 @@
package org.whispersystems.textsecuregcm.workers; package org.whispersystems.textsecuregcm.workers;
import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Namespace;
import net.spy.memcached.MemcachedClient;
import org.skife.jdbi.v2.DBI; import org.skife.jdbi.v2.DBI;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.WhisperServerConfiguration; import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
import org.whispersystems.textsecuregcm.federation.FederatedClientManager; import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
import org.whispersystems.textsecuregcm.providers.MemcachedClientFactory;
import org.whispersystems.textsecuregcm.providers.RedisClientFactory; import org.whispersystems.textsecuregcm.providers.RedisClientFactory;
import org.whispersystems.textsecuregcm.storage.Accounts; import org.whispersystems.textsecuregcm.storage.Accounts;
import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.AccountsManager;
@ -62,10 +60,10 @@ public class DirectoryCommand extends ConfiguredCommand<WhisperServerConfigurati
dbi.registerContainerFactory(new OptionalContainerFactory()); dbi.registerContainerFactory(new OptionalContainerFactory());
Accounts accounts = dbi.onDemand(Accounts.class); Accounts accounts = dbi.onDemand(Accounts.class);
MemcachedClient memcachedClient = new MemcachedClientFactory(config.getMemcacheConfiguration()).getClient(); JedisPool cacheClient = new RedisClientFactory(config.getCacheConfiguration().getUrl()).getRedisClientPool();
JedisPool redisClient = new RedisClientFactory(config.getDirectoryConfiguration().getUrl()).getRedisClientPool(); JedisPool redisClient = new RedisClientFactory(config.getDirectoryConfiguration().getUrl()).getRedisClientPool();
DirectoryManager directory = new DirectoryManager(redisClient); DirectoryManager directory = new DirectoryManager(redisClient);
AccountsManager accountsManager = new AccountsManager(accounts, directory, memcachedClient); AccountsManager accountsManager = new AccountsManager(accounts, directory, cacheClient);
FederatedClientManager federatedClientManager = new FederatedClientManager(config.getFederationConfiguration()); FederatedClientManager federatedClientManager = new FederatedClientManager(config.getFederationConfiguration());
DirectoryUpdater update = new DirectoryUpdater(accountsManager, federatedClientManager, directory); DirectoryUpdater update = new DirectoryUpdater(accountsManager, federatedClientManager, directory);