parent
							
								
									9d3d9d1390
								
							
						
					
					
						commit
						a297d03db5
					
				|  | @ -85,15 +85,14 @@ public class WhisperServerConfiguration extends Configuration { | ||||||
|   @JsonProperty |   @JsonProperty | ||||||
|   private DataSourceFactory database = new DataSourceFactory(); |   private DataSourceFactory database = new DataSourceFactory(); | ||||||
| 
 | 
 | ||||||
|  |   @JsonProperty | ||||||
|  |   private DataSourceFactory read_database; | ||||||
|  | 
 | ||||||
|   @Valid |   @Valid | ||||||
|   @NotNull |   @NotNull | ||||||
|   @JsonProperty |   @JsonProperty | ||||||
|   private RateLimitsConfiguration limits = new RateLimitsConfiguration(); |   private RateLimitsConfiguration limits = new RateLimitsConfiguration(); | ||||||
| 
 | 
 | ||||||
|   @Valid |  | ||||||
|   @JsonProperty |  | ||||||
|   private GraphiteConfiguration graphite = new GraphiteConfiguration(); |  | ||||||
| 
 |  | ||||||
|   @Valid |   @Valid | ||||||
|   @JsonProperty |   @JsonProperty | ||||||
|   private WebsocketConfiguration websocket = new WebsocketConfiguration(); |   private WebsocketConfiguration websocket = new WebsocketConfiguration(); | ||||||
|  | @ -143,6 +142,10 @@ public class WhisperServerConfiguration extends Configuration { | ||||||
|     return database; |     return database; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   public DataSourceFactory getReadDataSourceFactory() { | ||||||
|  |     return read_database; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   public RateLimitsConfiguration getLimitsConfiguration() { |   public RateLimitsConfiguration getLimitsConfiguration() { | ||||||
|     return limits; |     return limits; | ||||||
|   } |   } | ||||||
|  | @ -151,10 +154,6 @@ public class WhisperServerConfiguration extends Configuration { | ||||||
|     return federation; |     return federation; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public GraphiteConfiguration getGraphiteConfiguration() { |  | ||||||
|     return graphite; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public RedPhoneConfiguration getRedphoneConfiguration() { |   public RedPhoneConfiguration getRedphoneConfiguration() { | ||||||
|     return redphone; |     return redphone; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -88,6 +88,7 @@ import org.whispersystems.textsecuregcm.websocket.DeadLetterHandler; | ||||||
| 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.workers.PeriodicStatsCommand; | ||||||
| import org.whispersystems.textsecuregcm.workers.TrimMessagesCommand; | import org.whispersystems.textsecuregcm.workers.TrimMessagesCommand; | ||||||
| import org.whispersystems.textsecuregcm.workers.VacuumCommand; | import org.whispersystems.textsecuregcm.workers.VacuumCommand; | ||||||
| import org.whispersystems.websocket.WebSocketResourceProviderFactory; | import org.whispersystems.websocket.WebSocketResourceProviderFactory; | ||||||
|  | @ -122,6 +123,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration | ||||||
|     bootstrap.addCommand(new DirectoryCommand()); |     bootstrap.addCommand(new DirectoryCommand()); | ||||||
|     bootstrap.addCommand(new VacuumCommand()); |     bootstrap.addCommand(new VacuumCommand()); | ||||||
|     bootstrap.addCommand(new TrimMessagesCommand()); |     bootstrap.addCommand(new TrimMessagesCommand()); | ||||||
|  |     bootstrap.addCommand(new PeriodicStatsCommand()); | ||||||
|     bootstrap.addBundle(new NameableMigrationsBundle<WhisperServerConfiguration>("accountdb", "accountsdb.xml") { |     bootstrap.addBundle(new NameableMigrationsBundle<WhisperServerConfiguration>("accountdb", "accountsdb.xml") { | ||||||
|       @Override |       @Override | ||||||
|       public DataSourceFactory getDataSourceFactory(WhisperServerConfiguration configuration) { |       public DataSourceFactory getDataSourceFactory(WhisperServerConfiguration configuration) { | ||||||
|  | @ -269,15 +271,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration | ||||||
|     environment.metrics().register(name(NetworkSentGauge.class, "bytes_sent"), new NetworkSentGauge()); |     environment.metrics().register(name(NetworkSentGauge.class, "bytes_sent"), new NetworkSentGauge()); | ||||||
|     environment.metrics().register(name(NetworkReceivedGauge.class, "bytes_received"), new NetworkReceivedGauge()); |     environment.metrics().register(name(NetworkReceivedGauge.class, "bytes_received"), new NetworkReceivedGauge()); | ||||||
|     environment.metrics().register(name(FileDescriptorGauge.class, "fd_count"), new FileDescriptorGauge()); |     environment.metrics().register(name(FileDescriptorGauge.class, "fd_count"), new FileDescriptorGauge()); | ||||||
| 
 |  | ||||||
|     if (config.getGraphiteConfiguration().isEnabled()) { |  | ||||||
|       GraphiteReporterFactory graphiteReporterFactory = new GraphiteReporterFactory(); |  | ||||||
|       graphiteReporterFactory.setHost(config.getGraphiteConfiguration().getHost()); |  | ||||||
|       graphiteReporterFactory.setPort(config.getGraphiteConfiguration().getPort()); |  | ||||||
| 
 |  | ||||||
|       GraphiteReporter graphiteReporter = (GraphiteReporter) graphiteReporterFactory.build(environment.metrics()); |  | ||||||
|       graphiteReporter.start(15, TimeUnit.SECONDS); |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private Client initializeHttpClient(Environment environment, WhisperServerConfiguration config) { |   private Client initializeHttpClient(Environment environment, WhisperServerConfiguration config) { | ||||||
|  |  | ||||||
|  | @ -56,7 +56,7 @@ public class JsonMetricsReporter extends ScheduledReporter { | ||||||
|                      SortedMap<String, Timer>     stringTimerSortedMap) |                      SortedMap<String, Timer>     stringTimerSortedMap) | ||||||
|   { |   { | ||||||
|     try { |     try { | ||||||
|       logger.debug("Reporting metrics..."); |       logger.info("Reporting metrics..."); | ||||||
|       URL url = new URL("https", hostname, 443, String.format("/report/metrics?t=%s&h=%s", token, host)); |       URL url = new URL("https", hostname, 443, String.format("/report/metrics?t=%s&h=%s", token, host)); | ||||||
|       HttpURLConnection connection = (HttpURLConnection) url.openConnection(); |       HttpURLConnection connection = (HttpURLConnection) url.openConnection(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -77,6 +77,12 @@ public abstract class Accounts { | ||||||
|   @SqlQuery("SELECT * FROM accounts") |   @SqlQuery("SELECT * FROM accounts") | ||||||
|   public abstract Iterator<Account> getAll(); |   public abstract Iterator<Account> getAll(); | ||||||
| 
 | 
 | ||||||
|  |   @SqlQuery("SELECT COUNT(*) FROM accounts a, json_array_elements(a.data->'devices') devices WHERE devices->>'id' = '1' AND (devices->>'lastSeen')\\:\\:bigint >= :since") | ||||||
|  |   public abstract int getActiveSinceCount(@Bind("since") long since); | ||||||
|  | 
 | ||||||
|  |   @SqlQuery("SELECT count(*) FROM accounts a, json_array_elements(a.data->'devices') devices WHERE devices->>'id' = '1' AND (devices->>'lastSeen')\\:\\:bigint >= :since AND (devices->>'signedPreKey') is null AND (devices->>'gcmId') is not null") | ||||||
|  |   public abstract int getUnsignedKeysCount(@Bind("since") long since); | ||||||
|  | 
 | ||||||
|   @Transaction(TransactionIsolationLevel.SERIALIZABLE) |   @Transaction(TransactionIsolationLevel.SERIALIZABLE) | ||||||
|   public long create(Account account) { |   public long create(Account account) { | ||||||
|     removeAccount(account.getNumber()); |     removeAccount(account.getNumber()); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,128 @@ | ||||||
|  | package org.whispersystems.textsecuregcm.workers; | ||||||
|  | 
 | ||||||
|  | import com.codahale.metrics.Gauge; | ||||||
|  | import com.codahale.metrics.ScheduledReporter; | ||||||
|  | import com.codahale.metrics.SharedMetricRegistries; | ||||||
|  | import com.fasterxml.jackson.databind.DeserializationFeature; | ||||||
|  | import net.sourceforge.argparse4j.inf.Namespace; | ||||||
|  | import org.skife.jdbi.v2.DBI; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  | import org.whispersystems.textsecuregcm.WhisperServerConfiguration; | ||||||
|  | import org.whispersystems.textsecuregcm.metrics.JsonMetricsReporter; | ||||||
|  | import org.whispersystems.textsecuregcm.metrics.JsonMetricsReporterFactory; | ||||||
|  | import org.whispersystems.textsecuregcm.storage.Accounts; | ||||||
|  | import org.whispersystems.textsecuregcm.util.Constants; | ||||||
|  | 
 | ||||||
|  | import java.util.concurrent.TimeUnit; | ||||||
|  | 
 | ||||||
|  | import static com.codahale.metrics.MetricRegistry.name; | ||||||
|  | import io.dropwizard.Application; | ||||||
|  | import io.dropwizard.cli.EnvironmentCommand; | ||||||
|  | import io.dropwizard.db.DataSourceFactory; | ||||||
|  | import io.dropwizard.jdbi.ImmutableListContainerFactory; | ||||||
|  | import io.dropwizard.jdbi.ImmutableSetContainerFactory; | ||||||
|  | import io.dropwizard.jdbi.OptionalContainerFactory; | ||||||
|  | import io.dropwizard.jdbi.args.OptionalArgumentFactory; | ||||||
|  | import io.dropwizard.metrics.ReporterFactory; | ||||||
|  | import io.dropwizard.setup.Environment; | ||||||
|  | 
 | ||||||
|  | public class PeriodicStatsCommand extends EnvironmentCommand<WhisperServerConfiguration> { | ||||||
|  | 
 | ||||||
|  |   private final Logger logger = LoggerFactory.getLogger(PeriodicStatsCommand.class); | ||||||
|  | 
 | ||||||
|  |   public PeriodicStatsCommand() { | ||||||
|  |     super(new Application<WhisperServerConfiguration>() { | ||||||
|  |       @Override | ||||||
|  |       public void run(WhisperServerConfiguration configuration, Environment environment) | ||||||
|  |           throws Exception | ||||||
|  |       { | ||||||
|  | 
 | ||||||
|  |       } | ||||||
|  |     }, "stats", "Update periodic stats."); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @Override | ||||||
|  |   protected void run(Environment environment, Namespace namespace, | ||||||
|  |                      WhisperServerConfiguration configuration) | ||||||
|  |       throws Exception | ||||||
|  |   { | ||||||
|  |     try { | ||||||
|  |       environment.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); | ||||||
|  | 
 | ||||||
|  |       DataSourceFactory dbConfig = configuration.getReadDataSourceFactory(); | ||||||
|  | 
 | ||||||
|  |       if (dbConfig == null) { | ||||||
|  |         logger.warn("No slave database configuration found!"); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       DBI dbi = new DBI(dbConfig.getUrl(), dbConfig.getUser(), dbConfig.getPassword()); | ||||||
|  |       dbi.registerArgumentFactory(new OptionalArgumentFactory(dbConfig.getDriverClass())); | ||||||
|  |       dbi.registerContainerFactory(new ImmutableListContainerFactory()); | ||||||
|  |       dbi.registerContainerFactory(new ImmutableSetContainerFactory()); | ||||||
|  |       dbi.registerContainerFactory(new OptionalContainerFactory()); | ||||||
|  | 
 | ||||||
|  |       Accounts accounts  = dbi.onDemand(Accounts.class); | ||||||
|  |       long     yesterday = TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis()) - 1; | ||||||
|  |       long     monthAgo  = yesterday - 30; | ||||||
|  | 
 | ||||||
|  |       logger.info("Calculating daily active"); | ||||||
|  |       final int dailyActive   = accounts.getActiveSinceCount(TimeUnit.DAYS.toMillis(yesterday)); | ||||||
|  |       logger.info("Calculating monthly active"); | ||||||
|  |       final int monthlyActive = accounts.getActiveSinceCount(TimeUnit.DAYS.toMillis(monthAgo)); | ||||||
|  | 
 | ||||||
|  |       logger.info("Calculating daily signed keys"); | ||||||
|  |       final int dailyActiveNoSignedKeys   = accounts.getUnsignedKeysCount(TimeUnit.DAYS.toMillis(yesterday)); | ||||||
|  |       logger.info("Calculating monthly signed keys"); | ||||||
|  |       final int monthlyActiveNoSignedKeys = accounts.getUnsignedKeysCount(TimeUnit.DAYS.toMillis(monthAgo )); | ||||||
|  | 
 | ||||||
|  |       environment.metrics().register(name(PeriodicStatsCommand.class, "daily_active"), | ||||||
|  |                                      new Gauge<Integer>() { | ||||||
|  |                                        @Override | ||||||
|  |                                        public Integer getValue() { | ||||||
|  |                                          return dailyActive; | ||||||
|  |                                        } | ||||||
|  |                                      }); | ||||||
|  | 
 | ||||||
|  |       environment.metrics().register(name(PeriodicStatsCommand.class, "monthly_active"), | ||||||
|  |                                      new Gauge<Integer>() { | ||||||
|  |                                        @Override | ||||||
|  |                                        public Integer getValue() { | ||||||
|  |                                          return monthlyActive; | ||||||
|  |                                        } | ||||||
|  |                                      }); | ||||||
|  | 
 | ||||||
|  |       environment.metrics().register(name(PeriodicStatsCommand.class, "daily_no_signed_keys"), | ||||||
|  |                                      new Gauge<Integer>() { | ||||||
|  |                                        @Override | ||||||
|  |                                        public Integer getValue() { | ||||||
|  |                                          return dailyActiveNoSignedKeys; | ||||||
|  |                                        } | ||||||
|  |                                      }); | ||||||
|  | 
 | ||||||
|  |       environment.metrics().register(name(PeriodicStatsCommand.class, "monthly_no_signed_keys"), | ||||||
|  |                                      new Gauge<Integer>() { | ||||||
|  |                                        @Override | ||||||
|  |                                        public Integer getValue() { | ||||||
|  |                                          return monthlyActiveNoSignedKeys; | ||||||
|  |                                        } | ||||||
|  |                                      }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |       for (ReporterFactory reporterFactory : configuration.getMetricsFactory().getReporters()) { | ||||||
|  |         ScheduledReporter reporter = reporterFactory.build(environment.metrics()); | ||||||
|  |         logger.info("Reporting via: " + reporter); | ||||||
|  |         reporter.report(); | ||||||
|  |         logger.info("Reporting finished..."); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |     } catch (Exception ex) { | ||||||
|  |       logger.warn("Directory Exception", ex); | ||||||
|  |       throw new RuntimeException(ex); | ||||||
|  |     } finally { | ||||||
|  |       Thread.sleep(3000); | ||||||
|  |       System.exit(0); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	 Moxie Marlinspike
						Moxie Marlinspike