Commit 75b77f09f8e4e2535988adc1309da24bfbb5c1ef
Committed by
GitHub
Merge pull request #1014 from davidgin/fixedPartitions
address issue Add partition granularity to time series data #1006 h…
Showing
3 changed files
with
32 additions
and
11 deletions
... | ... | @@ -205,7 +205,7 @@ cassandra: |
205 | 205 | read_consistency_level: "${CASSANDRA_READ_CONSISTENCY_LEVEL:ONE}" |
206 | 206 | write_consistency_level: "${CASSANDRA_WRITE_CONSISTENCY_LEVEL:ONE}" |
207 | 207 | default_fetch_size: "${CASSANDRA_DEFAULT_FETCH_SIZE:2000}" |
208 | - # Specify partitioning size for timestamp key-value storage. Example MINUTES, HOURS, DAYS, MONTHS | |
208 | + # Specify partitioning size for timestamp key-value storage. Example MINUTES, HOURS, DAYS, MONTHS,INDEFINITE | |
209 | 209 | ts_key_value_partitioning: "${TS_KV_PARTITIONING:MONTHS}" |
210 | 210 | ts_key_value_ttl: "${TS_KV_TTL:0}" |
211 | 211 | buffer_size: "${CASSANDRA_QUERY_BUFFER_SIZE:200000}" | ... | ... |
... | ... | @@ -54,10 +54,11 @@ import javax.annotation.PreDestroy; |
54 | 54 | import java.time.Instant; |
55 | 55 | import java.time.LocalDateTime; |
56 | 56 | import java.time.ZoneOffset; |
57 | -import java.util.ArrayList; | |
58 | -import java.util.Collections; | |
57 | +import java.util.Arrays; | |
59 | 58 | import java.util.List; |
59 | +import java.util.ArrayList; | |
60 | 60 | import java.util.Optional; |
61 | +import java.util.Collections; | |
61 | 62 | import java.util.stream.Collectors; |
62 | 63 | |
63 | 64 | import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; |
... | ... | @@ -76,6 +77,9 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem |
76 | 77 | public static final String SELECT_PREFIX = "SELECT "; |
77 | 78 | public static final String EQUALS_PARAM = " = ? "; |
78 | 79 | |
80 | + | |
81 | + private static List<Long> FIXED_PARTITION = Arrays.asList(new Long[]{0L}); | |
82 | + | |
79 | 83 | @Autowired |
80 | 84 | private Environment environment; |
81 | 85 | |
... | ... | @@ -163,14 +167,25 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem |
163 | 167 | } |
164 | 168 | } |
165 | 169 | |
170 | + public boolean isFixedPartitioning() { | |
171 | + return tsFormat.getTruncateUnit().equals(TsPartitionDate.EPOCH_START); | |
172 | + } | |
173 | + | |
174 | + private ListenableFuture<List<Long>> getPartitionsFuture(TsKvQuery query, EntityId entityId, long minPartition, long maxPartition) { | |
175 | + if (isFixedPartitioning()) { //no need to fetch partitions from DB | |
176 | + return Futures.immediateFuture(FIXED_PARTITION); | |
177 | + } | |
178 | + ResultSetFuture partitionsFuture = fetchPartitions(entityId, query.getKey(), minPartition, maxPartition); | |
179 | + return Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor); | |
180 | + } | |
181 | + | |
166 | 182 | private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, TsKvQuery query) { |
183 | + | |
167 | 184 | long minPartition = toPartitionTs(query.getStartTs()); |
168 | 185 | long maxPartition = toPartitionTs(query.getEndTs()); |
169 | 186 | |
170 | - ResultSetFuture partitionsFuture = fetchPartitions(entityId, query.getKey(), minPartition, maxPartition); | |
171 | - | |
187 | + final ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(query, entityId, minPartition, maxPartition); | |
172 | 188 | final SimpleListenableFuture<List<TsKvEntry>> resultFuture = new SimpleListenableFuture<>(); |
173 | - final ListenableFuture<List<Long>> partitionsListFuture = Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor); | |
174 | 189 | |
175 | 190 | Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() { |
176 | 191 | @Override |
... | ... | @@ -181,7 +196,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem |
181 | 196 | |
182 | 197 | @Override |
183 | 198 | public void onFailure(Throwable t) { |
184 | - log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), minPartition, maxPartition, t); | |
199 | + log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), toPartitionTs(query.getStartTs()), toPartitionTs(query.getEndTs()), t); | |
185 | 200 | } |
186 | 201 | }, readResultsProcessingExecutor); |
187 | 202 | |
... | ... | @@ -229,10 +244,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem |
229 | 244 | final long endTs = query.getEndTs(); |
230 | 245 | final long ts = startTs + (endTs - startTs) / 2; |
231 | 246 | |
232 | - ResultSetFuture partitionsFuture = fetchPartitions(entityId, key, minPartition, maxPartition); | |
233 | - | |
234 | - ListenableFuture<List<Long>> partitionsListFuture = Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor); | |
235 | 247 | |
248 | + ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(query, entityId, minPartition, maxPartition); | |
236 | 249 | ListenableFuture<List<ResultSet>> aggregationChunks = Futures.transformAsync(partitionsListFuture, |
237 | 250 | getFetchChunksAsyncFunction(entityId, key, aggregation, startTs, endTs), readResultsProcessingExecutor); |
238 | 251 | |
... | ... | @@ -308,6 +321,9 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem |
308 | 321 | |
309 | 322 | @Override |
310 | 323 | public ListenableFuture<Void> savePartition(EntityId entityId, long tsKvEntryTs, String key, long ttl) { |
324 | + if (isFixedPartitioning()) { | |
325 | + return Futures.immediateFuture(null); | |
326 | + } | |
311 | 327 | ttl = computeTtl(ttl); |
312 | 328 | long partition = toPartitionTs(tsKvEntryTs); |
313 | 329 | log.debug("Saving partition {} for the entity [{}-{}] and key {}", partition, entityId.getEntityType(), entityId.getId(), key); | ... | ... |
... | ... | @@ -16,22 +16,25 @@ |
16 | 16 | package org.thingsboard.server.dao.timeseries; |
17 | 17 | |
18 | 18 | import java.time.LocalDateTime; |
19 | +import java.time.ZoneOffset; | |
19 | 20 | import java.time.temporal.ChronoUnit; |
20 | 21 | import java.time.temporal.TemporalUnit; |
21 | 22 | import java.util.Optional; |
22 | 23 | |
23 | 24 | public enum TsPartitionDate { |
24 | 25 | |
25 | - MINUTES("yyyy-MM-dd-HH-mm", ChronoUnit.MINUTES), HOURS("yyyy-MM-dd-HH", ChronoUnit.HOURS), DAYS("yyyy-MM-dd", ChronoUnit.DAYS), MONTHS("yyyy-MM", ChronoUnit.MONTHS), YEARS("yyyy", ChronoUnit.YEARS); | |
26 | + MINUTES("yyyy-MM-dd-HH-mm", ChronoUnit.MINUTES), HOURS("yyyy-MM-dd-HH", ChronoUnit.HOURS), DAYS("yyyy-MM-dd", ChronoUnit.DAYS), MONTHS("yyyy-MM", ChronoUnit.MONTHS), YEARS("yyyy", ChronoUnit.YEARS),INDEFINITE("",ChronoUnit.FOREVER); | |
26 | 27 | |
27 | 28 | private final String pattern; |
28 | 29 | private final transient TemporalUnit truncateUnit; |
30 | + public final static LocalDateTime EPOCH_START = LocalDateTime.ofEpochSecond(0,0, ZoneOffset.UTC); | |
29 | 31 | |
30 | 32 | TsPartitionDate(String pattern, TemporalUnit truncateUnit) { |
31 | 33 | this.pattern = pattern; |
32 | 34 | this.truncateUnit = truncateUnit; |
33 | 35 | } |
34 | 36 | |
37 | + | |
35 | 38 | public String getPattern() { |
36 | 39 | return pattern; |
37 | 40 | } |
... | ... | @@ -46,6 +49,8 @@ public enum TsPartitionDate { |
46 | 49 | return time.truncatedTo(ChronoUnit.DAYS).withDayOfMonth(1); |
47 | 50 | case YEARS: |
48 | 51 | return time.truncatedTo(ChronoUnit.DAYS).withDayOfYear(1); |
52 | + case INDEFINITE: | |
53 | + return EPOCH_START; | |
49 | 54 | default: |
50 | 55 | return time.truncatedTo(truncateUnit); |
51 | 56 | } | ... | ... |