/* * Copyright 2016-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.glowroot.central.repo; import java.util.List; import javax.annotation.Nullable; import com.datastax.driver.core.BoundStatement; import com.datastax.driver.core.PreparedStatement; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSetFuture; import com.datastax.driver.core.Row; import com.datastax.driver.core.Session; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.primitives.Ints; import org.immutables.value.Value; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.glowroot.central.util.RateLimiter; import org.glowroot.central.util.Sessions; import org.glowroot.common.repo.ConfigRepository; import org.glowroot.common.repo.ConfigRepository.RollupConfig; import org.glowroot.common.util.Styles; import static com.google.common.base.Preconditions.checkNotNull; import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.HOURS; import static java.util.concurrent.TimeUnit.MILLISECONDS; public class FullQueryTextDao { private static final Logger logger = LoggerFactory.getLogger(FullQueryTextDao.class); private final Session session; private final ConfigRepository configRepository; private final PreparedStatement insertCheckPS; private final PreparedStatement readCheckPS; private final PreparedStatement insertPS; private final PreparedStatement readPS; private final RateLimiter<FullQueryTextKey> rateLimiter = new RateLimiter<>(10000); public FullQueryTextDao(Session session, ConfigRepository configRepository) { this.session = session; this.configRepository = configRepository; // intentionally using default size-tiered compaction strategy session.execute("create table if not exists full_query_text_check (agent_rollup varchar," + " full_query_text_sha1 varchar, primary key (agent_rollup," + " full_query_text_sha1))"); session.execute("create table if not exists full_query_text (full_query_text_sha1 varchar," + " full_query_text varchar, primary key (full_query_text_sha1))"); insertCheckPS = session.prepare("insert into full_query_text_check (agent_rollup," + " full_query_text_sha1) values (?, ?) using ttl ?"); readCheckPS = session.prepare("select agent_rollup from full_query_text_check" + " where agent_rollup = ? and full_query_text_sha1 = ?"); insertPS = session.prepare("insert into full_query_text (full_query_text_sha1," + " full_query_text) values (?, ?) using ttl ?"); readPS = session.prepare( "select full_query_text from full_query_text where full_query_text_sha1 = ?"); } @Nullable String getFullText(String agentRollupId, String fullTextSha1) { BoundStatement boundStatement = readCheckPS.bind(); boundStatement.setString(0, agentRollupId); boundStatement.setString(1, fullTextSha1); ResultSet results = session.execute(boundStatement); if (results.isExhausted()) { return null; } boundStatement = readPS.bind(); boundStatement.setString(0, fullTextSha1); results = session.execute(boundStatement); Row row = results.one(); if (row == null) { return null; } return row.getString(0); } List<ResultSetFuture> store(String agentRollupId, String fullTextSha1, String fullText) throws Exception { FullQueryTextKey rateLimiterKey = ImmutableFullQueryTextKey.of(agentRollupId, fullTextSha1); if (!rateLimiter.tryAcquire(rateLimiterKey)) { return ImmutableList.of(); } return storeInternal(rateLimiterKey, fullText); } List<ResultSetFuture> updateTTL(String agentRollupId, String fullTextSha1) throws Exception { FullQueryTextKey rateLimiterKey = ImmutableFullQueryTextKey.of(agentRollupId, fullTextSha1); if (!rateLimiter.tryAcquire(rateLimiterKey)) { return ImmutableList.of(); } BoundStatement boundStatement = readPS.bind(); boundStatement.setString(0, fullTextSha1); ResultSet results = session.execute(boundStatement); Row row = results.one(); if (row == null) { // this shouldn't happen any more now that full query text insert futures are waited on // prior to inserting aggregate/trace records with sha1 logger.warn("full query text record not found for sha1: {}", fullTextSha1); return ImmutableList.of(); } String fullText = checkNotNull(row.getString(0)); return storeInternal(rateLimiterKey, fullText); } List<ResultSetFuture> updateCheckTTL(String agentRollupId, String fullTextSha1) throws Exception { FullQueryTextKey rateLimiterKey = ImmutableFullQueryTextKey.of(agentRollupId, fullTextSha1); if (!rateLimiter.tryAcquire(rateLimiterKey)) { return ImmutableList.of(); } List<ResultSetFuture> futures = Lists.newArrayList(); storeCheckInternal(rateLimiterKey); return futures; } private List<ResultSetFuture> storeInternal(FullQueryTextKey rateLimiterKey, String fullText) throws Exception { List<ResultSetFuture> futures = Lists.newArrayList(); futures.add(storeCheckInternal(rateLimiterKey)); BoundStatement boundStatement = insertPS.bind(); int i = 0; boundStatement.setString(i++, rateLimiterKey.fullTextSha1()); boundStatement.setString(i++, fullText); boundStatement.setInt(i++, getTTL()); futures.add(Sessions.executeAsyncWithOnFailure(session, boundStatement, () -> rateLimiter.invalidate(rateLimiterKey))); return futures; } private ResultSetFuture storeCheckInternal(FullQueryTextKey rateLimiterKey) throws Exception { BoundStatement boundStatement = insertCheckPS.bind(); int i = 0; boundStatement.setString(i++, rateLimiterKey.agentRollupId()); boundStatement.setString(i++, rateLimiterKey.fullTextSha1()); boundStatement.setInt(i++, getTTL()); return Sessions.executeAsyncWithOnFailure(session, boundStatement, () -> rateLimiter.invalidate(rateLimiterKey)); } private int getTTL() throws Exception { List<RollupConfig> rollupConfigs = configRepository.getRollupConfigs(); RollupConfig lastRollupConfig = rollupConfigs.get(rollupConfigs.size() - 1); // adding largest rollup time to account for query being retained longer by rollups long ttl = MILLISECONDS.toSeconds(lastRollupConfig.intervalMillis()) // adding 1 day to account for rateLimiter + DAYS.toSeconds(1) + HOURS.toSeconds( configRepository.getStorageConfig().fullQueryTextExpirationHours()); return Ints.saturatedCast(ttl); } @Value.Immutable @Styles.AllParameters interface FullQueryTextKey { String agentRollupId(); String fullTextSha1(); } }