blob: 3e9e8e9b298e23638e6c4382dacf856320d0f96d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.skywalking.oap.server.storage.plugin.elasticsearch.base;
import java.util.ArrayList;
import java.util.List;
import lombok.Setter;
import org.apache.skywalking.oap.server.core.Const;
import org.apache.skywalking.oap.server.core.UnexpectedException;
import org.apache.skywalking.oap.server.core.analysis.DownSampling;
import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
import org.apache.skywalking.oap.server.core.query.enumeration.Step;
import org.apache.skywalking.oap.server.core.storage.model.Model;
import org.apache.skywalking.oap.server.library.util.StringUtil;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
/**
* TimeSeriesUtils sets up and splits the time suffix of index name.
*/
public class TimeSeriesUtils {
private static DateTimeFormatter TIME_BUCKET_FORMATTER = DateTimeFormat.forPattern("yyyyMMdd");
/**
* We are far from the first day of 2000, so we set it as the day one to make sure the index based on {@link
* #DAY_STEP} is consistently no matter whenever the OAP starts up.
*/
private static final DateTime DAY_ONE = TIME_BUCKET_FORMATTER.parseDateTime("20000101");
@Setter
private static int DAY_STEP = 1;
@Setter
private static int SUPER_DATASET_DAY_STEP = 1;
/**
* @return formatted latest index name, based on current timestamp.
*/
public static String latestWriteIndexName(Model model) {
String tableName = IndexController.INSTANCE.getTableName(model);
long dayTimeBucket = TimeBucket.getTimeBucket(System.currentTimeMillis(), DownSampling.Day);
if (model.isRecord() && model.isSuperDataset()) {
return tableName + Const.LINE + compressTimeBucket(dayTimeBucket, SUPER_DATASET_DAY_STEP);
}
return tableName + Const.LINE + compressTimeBucket(dayTimeBucket, DAY_STEP);
}
/**
* @return Concrete index name for super dataset index
*/
public static String[] superDatasetIndexNames(String indexName, long startSecondTB, long endSecondTB) {
if (startSecondTB == 0 || endSecondTB == 0) {
return new String[] {indexName};
}
DateTime startDateTime = TIME_BUCKET_FORMATTER.parseDateTime(startSecondTB / 1000000 + "");
DateTime endDateTime = TIME_BUCKET_FORMATTER.parseDateTime(endSecondTB / 1000000 + "");
List<DateTime> timeRanges = new ArrayList<>(16);
for (int i = 0; i <= Days.daysBetween(startDateTime, endDateTime).getDays(); i++) {
timeRanges.add(startDateTime.plusDays(i));
}
if (timeRanges.isEmpty()) {
return new String[] {indexName};
} else {
return timeRanges.stream()
.map(item -> indexName + Const.LINE + compressDateTime(item, SUPER_DATASET_DAY_STEP))
.distinct()
.toArray(String[]::new);
}
}
public static String queryIndexName(String tableName,
long pointOfTB,
Step step,
boolean isRecord,
boolean isSuperDataSet) {
if (StringUtil.isBlank(tableName) || pointOfTB <= 0) {
throw new IllegalArgumentException(
"Arguments [tableName]: " + tableName + " can not be blank and [pointOfTB]: " + pointOfTB + " can not <= 0");
}
if (isRecord && isSuperDataSet) {
return tableName + Const.LINE + compressTimeBucket(pointOfTB / 1000000, SUPER_DATASET_DAY_STEP);
}
switch (step) {
case DAY:
return tableName + Const.LINE + compressTimeBucket(pointOfTB, DAY_STEP);
case HOUR:
return tableName + Const.LINE + compressTimeBucket(pointOfTB / 100, DAY_STEP);
case MINUTE:
return tableName + Const.LINE + compressTimeBucket(pointOfTB / 10000, DAY_STEP);
case SECOND:
return tableName + Const.LINE + compressTimeBucket(pointOfTB / 1000000, DAY_STEP);
}
throw new UnexpectedException("Failed to get the index name from tableName:" + tableName + ", pointOfTB:" + pointOfTB + ", step:" + step.name());
}
/**
* @return index name based on model definition and given time bucket.
*/
static String writeIndexName(Model model, long timeBucket) {
String tableName = IndexController.INSTANCE.getTableName(model);
if (model.isRecord() && model.isSuperDataset()) {
return tableName + Const.LINE + compressTimeBucket(timeBucket / 1000000, SUPER_DATASET_DAY_STEP);
} else {
switch (model.getDownsampling()) {
case None:
return tableName;
case Hour:
return tableName + Const.LINE + compressTimeBucket(timeBucket / 100, DAY_STEP);
case Minute:
return tableName + Const.LINE + compressTimeBucket(timeBucket / 10000, DAY_STEP);
case Day:
return tableName + Const.LINE + compressTimeBucket(timeBucket, DAY_STEP);
case Second:
return tableName + Const.LINE + compressTimeBucket(timeBucket / 1000000, DAY_STEP);
default:
throw new UnexpectedException("Unexpected down sampling value, " + model.getDownsampling());
}
}
}
/**
* @return the index represented time, which is included in the index name.
*/
static long isolateTimeFromIndexName(String indexName) {
return Long.parseLong(indexName.substring(indexName.lastIndexOf(Const.LINE) + 1));
}
/**
* Follow the dayStep to re-format the time bucket literal long value.
*
* Such as, in dayStep == 11,
*
* 20000105 re-formatted time bucket is 20000101, 20000115 re-formatted time bucket is 20000112, 20000123
* re-formatted time bucket is 20000123
*/
static long compressTimeBucket(long timeBucket, int dayStep) {
if (dayStep > 1) {
DateTime time = TIME_BUCKET_FORMATTER.parseDateTime("" + timeBucket);
int days = Days.daysBetween(DAY_ONE, time).getDays();
int groupBucketOffset = days % dayStep;
return Long.parseLong(time.minusDays(groupBucketOffset).toString(TIME_BUCKET_FORMATTER));
} else {
/*
* No calculation required. dayStep is for lower traffic. For normally configuration, there is pointless to calculate.
*/
return timeBucket;
}
}
static long compressDateTime(DateTime time, int dayStep) {
if (dayStep > 1) {
int days = Days.daysBetween(DAY_ONE, time).getDays();
int groupBucketOffset = days % dayStep;
return Long.parseLong(time.minusDays(groupBucketOffset).toString(TIME_BUCKET_FORMATTER));
} else {
/**
* No calculation required. dayStep is for lower traffic. For normally configuration, there is pointless to calculate.
*/
return Long.parseLong(time.toString(TIME_BUCKET_FORMATTER));
}
}
}