-
Notifications
You must be signed in to change notification settings - Fork 816
Allow switching to daily instead of hourly index buckets #178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ import ( | |
"math/rand" | ||
"net/url" | ||
"sort" | ||
"strconv" | ||
"strings" | ||
"sync" | ||
"sync/atomic" | ||
|
@@ -139,9 +140,10 @@ type Store interface { | |
|
||
// StoreConfig specifies config for a ChunkStore | ||
type StoreConfig struct { | ||
S3URL string | ||
DynamoDBURL string | ||
ChunkCache *Cache | ||
S3URL string | ||
DynamoDBURL string | ||
ChunkCache *Cache | ||
DailyBucketsFrom model.Time | ||
|
||
// Not exported as only used by tests to inject mocks | ||
dynamodb dynamodbClient | ||
|
@@ -206,6 +208,10 @@ type AWSStore struct { | |
tableName string | ||
bucketName string | ||
|
||
// Around which time to start bucketing indexes by day instead of by hour. | ||
// Only the day matters, not the time within the day. | ||
dailyBucketsFrom model.Time | ||
|
||
dynamoRequests chan dynamoOp | ||
dynamoRequestsDone sync.WaitGroup | ||
} | ||
|
@@ -332,25 +338,45 @@ func (c *AWSStore) CreateTables() error { | |
return err | ||
} | ||
|
||
func bigBuckets(from, through model.Time) []int64 { | ||
func (c *AWSStore) bigBuckets(from, through model.Time) []string { | ||
var ( | ||
secondsInHour = int64(time.Hour / time.Second) | ||
fromHour = from.Unix() / secondsInHour | ||
throughHour = through.Unix() / secondsInHour | ||
result []int64 | ||
|
||
secondsInDay = int64(24 * time.Hour / time.Second) | ||
fromDay = from.Unix() / secondsInDay | ||
throughDay = through.Unix() / secondsInDay | ||
|
||
firstDailyBucket = c.dailyBucketsFrom.Unix() / secondsInDay | ||
lastHourlyBucket = firstDailyBucket * 24 | ||
|
||
result []string | ||
) | ||
|
||
for i := fromHour; i <= throughHour; i++ { | ||
result = append(result, i) | ||
if i > lastHourlyBucket { | ||
break | ||
} | ||
result = append(result, strconv.Itoa(int(i))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure the conversion to int is safe here - they'll run out on 2038-01-19 IIRC. Should probably use https://golang.org/pkg/strconv/#FormatInt There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah ignore me, these are hours, so the range is huge. |
||
} | ||
|
||
for i := fromDay; i <= throughDay; i++ { | ||
if i < firstDailyBucket { | ||
continue | ||
} | ||
result = append(result, fmt.Sprintf("d%d", int(i))) | ||
} | ||
|
||
return result | ||
} | ||
|
||
func chunkName(userID, chunkID string) string { | ||
return fmt.Sprintf("%s/%s", userID, chunkID) | ||
} | ||
|
||
func hashValue(userID string, hour int64, metricName model.LabelValue) string { | ||
return fmt.Sprintf("%s:%d:%s", userID, hour, metricName) | ||
func hashValue(userID string, bucket string, metricName model.LabelValue) string { | ||
return fmt.Sprintf("%s:%s:%s", userID, bucket, metricName) | ||
} | ||
|
||
func rangeValue(label model.LabelName, value model.LabelValue, chunkID string) []byte { | ||
|
@@ -456,8 +482,8 @@ func (c *AWSStore) calculateDynamoWrites(userID string, chunks []Chunk) ([]*dyna | |
} | ||
|
||
entries := 0 | ||
for _, hour := range bigBuckets(chunk.From, chunk.Through) { | ||
hashValue := hashValue(userID, hour, metricName) | ||
for _, bucket := range c.bigBuckets(chunk.From, chunk.Through) { | ||
hashValue := hashValue(userID, bucket, metricName) | ||
for label, value := range chunk.Metric { | ||
if label == model.MetricNameLabel { | ||
continue | ||
|
@@ -556,18 +582,18 @@ func (c *AWSStore) lookupChunks(ctx context.Context, userID string, from, throug | |
|
||
incomingChunkSets := make(chan ByID) | ||
incomingErrors := make(chan error) | ||
buckets := bigBuckets(from, through) | ||
buckets := c.bigBuckets(from, through) | ||
totalLookups := int32(0) | ||
for _, hour := range buckets { | ||
go func(hour int64) { | ||
incoming, lookups, err := c.lookupChunksFor(ctx, userID, hour, metricName, matchers) | ||
for _, b := range buckets { | ||
go func(bucket string) { | ||
incoming, lookups, err := c.lookupChunksFor(ctx, userID, bucket, metricName, matchers) | ||
atomic.AddInt32(&totalLookups, lookups) | ||
if err != nil { | ||
incomingErrors <- err | ||
} else { | ||
incomingChunkSets <- incoming | ||
} | ||
}(hour) | ||
}(b) | ||
} | ||
|
||
var chunks ByID | ||
|
@@ -592,17 +618,17 @@ func next(s string) string { | |
return result | ||
} | ||
|
||
func (c *AWSStore) lookupChunksFor(ctx context.Context, userID string, hour int64, metricName model.LabelValue, matchers []*metric.LabelMatcher) (ByID, int32, error) { | ||
func (c *AWSStore) lookupChunksFor(ctx context.Context, userID string, bucket string, metricName model.LabelValue, matchers []*metric.LabelMatcher) (ByID, int32, error) { | ||
if len(matchers) == 0 { | ||
return c.lookupChunksForMetricName(ctx, userID, hour, metricName) | ||
return c.lookupChunksForMetricName(ctx, userID, bucket, metricName) | ||
} | ||
|
||
incomingChunkSets := make(chan ByID) | ||
incomingErrors := make(chan error) | ||
|
||
for _, matcher := range matchers { | ||
go func(matcher *metric.LabelMatcher) { | ||
incoming, err := c.lookupChunksForMatcher(ctx, userID, hour, metricName, matcher) | ||
incoming, err := c.lookupChunksForMatcher(ctx, userID, bucket, metricName, matcher) | ||
if err != nil { | ||
incomingErrors <- err | ||
} else { | ||
|
@@ -624,8 +650,8 @@ func (c *AWSStore) lookupChunksFor(ctx context.Context, userID string, hour int6 | |
return nWayIntersect(chunkSets), int32(len(matchers)), lastErr | ||
} | ||
|
||
func (c *AWSStore) lookupChunksForMetricName(ctx context.Context, userID string, hour int64, metricName model.LabelValue) (ByID, int32, error) { | ||
hashValue := hashValue(userID, hour, metricName) | ||
func (c *AWSStore) lookupChunksForMetricName(ctx context.Context, userID string, bucket string, metricName model.LabelValue) (ByID, int32, error) { | ||
hashValue := hashValue(userID, bucket, metricName) | ||
input := &dynamodb.QueryInput{ | ||
TableName: aws.String(c.tableName), | ||
KeyConditions: map[string]*dynamodb.Condition{ | ||
|
@@ -665,8 +691,8 @@ func (c *AWSStore) lookupChunksForMetricName(ctx context.Context, userID string, | |
return chunkSet, 1, nil | ||
} | ||
|
||
func (c *AWSStore) lookupChunksForMatcher(ctx context.Context, userID string, hour int64, metricName model.LabelValue, matcher *metric.LabelMatcher) (ByID, error) { | ||
hashValue := hashValue(userID, hour, metricName) | ||
func (c *AWSStore) lookupChunksForMatcher(ctx context.Context, userID string, bucket string, metricName model.LabelValue, matcher *metric.LabelMatcher) (ByID, error) { | ||
hashValue := hashValue(userID, bucket, metricName) | ||
var rangeMinValue, rangeMaxValue []byte | ||
if matcher.Type == metric.Equal { | ||
nextValue := model.LabelValue(next(string(matcher.Value))) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This variable is never initialised! Which means we'll default to weekly buckets for everything...
We should do two things: copy the config straight into the AWSStore struct, and change the meaning so the time-zero-value means no weekly buckets.
Will backout this PR.