Add timed metrics to accounts manager

This commit is contained in:
Moxie Marlinspike 2018-11-01 03:00:36 -07:00
parent bb0d26e116
commit 768b52e517
1 changed files with 106 additions and 75 deletions

View File

@ -17,6 +17,9 @@
package org.whispersystems.textsecuregcm.storage; package org.whispersystems.textsecuregcm.storage;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.Timer;
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.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommand;
@ -29,6 +32,7 @@ import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.entities.ClientContact; import org.whispersystems.textsecuregcm.entities.ClientContact;
import org.whispersystems.textsecuregcm.hystrix.GroupKeys; import org.whispersystems.textsecuregcm.hystrix.GroupKeys;
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool; import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
import org.whispersystems.textsecuregcm.util.Constants;
import org.whispersystems.textsecuregcm.util.SystemMapper; import org.whispersystems.textsecuregcm.util.SystemMapper;
import org.whispersystems.textsecuregcm.util.Util; import org.whispersystems.textsecuregcm.util.Util;
@ -36,12 +40,24 @@ import java.io.IOException;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import static com.codahale.metrics.MetricRegistry.name;
import redis.clients.jedis.Jedis; import redis.clients.jedis.Jedis;
public class AccountsManager { public class AccountsManager {
private static final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
private static final Timer createTimer = metricRegistry.timer(name(AccountsManager.class, "create" ));
private static final Timer updateTimer = metricRegistry.timer(name(AccountsManager.class, "update" ));
private static final Timer getTimer = metricRegistry.timer(name(AccountsManager.class, "get" ));
private static final Timer redisSetTimer = metricRegistry.timer(name(AccountsManager.class, "redisSet" ));
private static final Timer redisGetTimer = metricRegistry.timer(name(AccountsManager.class, "redisGet" ));
private static final Timer databaseCreateTimer = metricRegistry.timer(name(AccountsManager.class, "databaseCreate"));
private static final Timer databaseGetTimer = metricRegistry.timer(name(AccountsManager.class, "databaseGet" ));
private static final Timer databaseUpdateTimer = metricRegistry.timer(name(AccountsManager.class, "databaseUpdate"));
private final Logger logger = LoggerFactory.getLogger(AccountsManager.class); private final Logger logger = LoggerFactory.getLogger(AccountsManager.class);
private final Accounts accounts; private final Accounts accounts;
@ -69,28 +85,34 @@ public class AccountsManager {
} }
public boolean create(Account account) { public boolean create(Account account) {
boolean freshUser = databaseCreate(account); try (Timer.Context context = createTimer.time()) {
redisSet(account.getNumber(), account, false); boolean freshUser = databaseCreate(account);
updateDirectory(account); redisSet(account.getNumber(), account, false);
updateDirectory(account);
return freshUser; return freshUser;
}
} }
public void update(Account account) { public void update(Account account) {
redisSet(account.getNumber(), account, false); try (Timer.Context context = updateTimer.time()) {
databaseUpdate(account); redisSet(account.getNumber(), account, false);
updateDirectory(account); databaseUpdate(account);
updateDirectory(account);
}
} }
public Optional<Account> get(String number) { public Optional<Account> get(String number) {
Optional<Account> account = redisGet(number); try (Timer.Context context = getTimer.time()) {
Optional<Account> account = redisGet(number);
if (!account.isPresent()) { if (!account.isPresent()) {
account = databaseGet(number); account = databaseGet(number);
account.ifPresent(value -> redisSet(number, value, true)); account.ifPresent(value -> redisSet(number, value, true));
}
return account;
} }
return account;
} }
private void updateDirectory(Account account) { private void updateDirectory(Account account) {
@ -117,83 +139,92 @@ public class AccountsManager {
} }
private void redisSet(String number, Account account, boolean optional) { private void redisSet(String number, Account account, boolean optional) {
new HystrixCommand<Boolean>(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(GroupKeys.REDIS_CACHE)) try (Timer.Context context = redisSetTimer.time()) {
.andCommandKey(HystrixCommandKey.Factory.asKey(AccountsManager.class.getSimpleName() + ".redisSet"))) new HystrixCommand<Boolean>(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(GroupKeys.REDIS_CACHE))
{ .andCommandKey(HystrixCommandKey.Factory.asKey(AccountsManager.class.getSimpleName() + ".redisSet"))) {
@Override @Override
protected Boolean run() { protected Boolean run() {
try (Jedis jedis = cacheClient.getWriteResource()) { try (Jedis jedis = cacheClient.getWriteResource()) {
jedis.set(getKey(number), mapper.writeValueAsString(account)); jedis.set(getKey(number), mapper.writeValueAsString(account));
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new HystrixBadRequestException("Json processing error", e); throw new HystrixBadRequestException("Json processing error", e);
}
return true;
} }
return true; @Override
} protected Boolean getFallback() {
if (optional) return true;
@Override else return super.getFallback();
protected Boolean getFallback() { }
if (optional) return true; }.execute();
else return super.getFallback(); }
}
}.execute();
} }
private Optional<Account> redisGet(String number) { private Optional<Account> redisGet(String number) {
return new HystrixCommand<Optional<Account>>(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(GroupKeys.REDIS_CACHE)) try (Timer.Context context = redisGetTimer.time()) {
.andCommandKey(HystrixCommandKey.Factory.asKey(AccountsManager.class.getSimpleName() + ".redisGet"))) return new HystrixCommand<Optional<Account>>(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(GroupKeys.REDIS_CACHE))
{ .andCommandKey(HystrixCommandKey.Factory.asKey(AccountsManager.class.getSimpleName() + ".redisGet")))
@Override {
protected Optional<Account> run() { @Override
try (Jedis jedis = cacheClient.getReadResource()) { protected Optional<Account> run() {
String json = jedis.get(getKey(number)); try (Jedis jedis = cacheClient.getReadResource()) {
String json = jedis.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.empty(); else return Optional.empty();
} catch (IOException e) { } catch (IOException e) {
logger.warn("AccountsManager", "Deserialization error", e); logger.warn("AccountsManager", "Deserialization error", e);
return Optional.empty();
}
}
@Override
protected Optional<Account> getFallback() {
return Optional.empty(); return Optional.empty();
} }
} }.execute();
}
@Override
protected Optional<Account> getFallback() {
return Optional.empty();
}
}.execute();
} }
private Optional<Account> databaseGet(String number) { private Optional<Account> databaseGet(String number) {
return new HystrixCommand<Optional<Account>>(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(GroupKeys.DATABASE_ACCOUNTS)) try (Timer.Context context = databaseGetTimer.time()) {
.andCommandKey(HystrixCommandKey.Factory.asKey(AccountsManager.class.getSimpleName() + ".databaseGet"))) return new HystrixCommand<Optional<Account>>(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(GroupKeys.DATABASE_ACCOUNTS))
{ .andCommandKey(HystrixCommandKey.Factory.asKey(AccountsManager.class.getSimpleName() + ".databaseGet")))
@Override {
protected Optional<Account> run() { @Override
return Optional.ofNullable(accounts.get(number)); protected Optional<Account> run() {
} return Optional.ofNullable(accounts.get(number));
}.execute(); }
}.execute();
}
} }
private boolean databaseCreate(Account account) { private boolean databaseCreate(Account account) {
return new HystrixCommand<Boolean>(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(GroupKeys.DATABASE_ACCOUNTS)) try (Timer.Context context = databaseCreateTimer.time()) {
.andCommandKey(HystrixCommandKey.Factory.asKey(AccountsManager.class.getSimpleName() + ".databaseCreate"))) return new HystrixCommand<Boolean>(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(GroupKeys.DATABASE_ACCOUNTS))
{ .andCommandKey(HystrixCommandKey.Factory.asKey(AccountsManager.class.getSimpleName() + ".databaseCreate")))
@Override {
protected Boolean run() { @Override
return accounts.create(account); protected Boolean run() {
} return accounts.create(account);
}.execute(); }
}.execute();
}
} }
private void databaseUpdate(Account account) { private void databaseUpdate(Account account) {
new HystrixCommand<Void>(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(GroupKeys.DATABASE_ACCOUNTS)) try (Timer.Context context = databaseUpdateTimer.time()) {
.andCommandKey(HystrixCommandKey.Factory.asKey(AccountsManager.class.getSimpleName() + ".databaseUpdate"))) new HystrixCommand<Void>(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(GroupKeys.DATABASE_ACCOUNTS))
{ .andCommandKey(HystrixCommandKey.Factory.asKey(AccountsManager.class.getSimpleName() + ".databaseUpdate")))
@Override {
protected Void run() { @Override
accounts.update(account); protected Void run() {
return null; accounts.update(account);
} return null;
}.execute(); }
}.execute();
}
} }
} }