Skip to content
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

Add lru cache for query conversion #1398

Merged
merged 18 commits into from
Feb 26, 2019
Merged

Conversation

benraskin92
Copy link
Collaborator

W/O cache
screen shot 2019-02-21 at 1 06 07 pm

W/ cache
screen shot 2019-02-21 at 12 58 44 pm

@codecov
Copy link

codecov bot commented Feb 21, 2019

Codecov Report

Merging #1398 into master will decrease coverage by 31.5%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff            @@
##           master   #1398      +/-   ##
=========================================
- Coverage    70.7%   39.1%   -31.6%     
=========================================
  Files         827       5     -822     
  Lines       71238     342   -70896     
=========================================
- Hits        50414     134   -50280     
+ Misses      17519     194   -17325     
+ Partials     3305      14    -3291
Flag Coverage Δ
#aggregator ?
#cluster ?
#collector 39.1% <ø> (-24.6%) ⬇️
#dbnode ?
#m3em ?
#m3ninx ?
#m3nsch ?
#metrics ?
#msg ?
#query ?
#x ?

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 667f864...c007e30. Read the comment docs.

@codecov
Copy link

codecov bot commented Feb 21, 2019

Codecov Report

Merging #1398 into master will decrease coverage by 7.6%.
The diff coverage is 60.6%.

Impacted file tree graph

@@           Coverage Diff            @@
##           master   #1398     +/-   ##
========================================
- Coverage    70.8%   63.1%   -7.7%     
========================================
  Files         832     831      -1     
  Lines       71370   71248    -122     
========================================
- Hits        50542   45001   -5541     
- Misses      17526   23103   +5577     
+ Partials     3302    3144    -158
Flag Coverage Δ
#aggregator 69.2% <ø> (-13.2%) ⬇️
#cluster 67.7% <ø> (-18.2%) ⬇️
#collector 47.9% <ø> (-15.8%) ⬇️
#dbnode 79.6% <ø> (-1.2%) ⬇️
#m3em 66.7% <ø> (-6.5%) ⬇️
#m3ninx 70.9% <ø> (-3.4%) ⬇️
#m3nsch 28.4% <ø> (-22.8%) ⬇️
#metrics 17.6% <ø> (ø) ⬆️
#msg 74.9% <ø> (ø) ⬆️
#query 46.7% <60.6%> (-18.9%) ⬇️
#x 68.1% <ø> (-8%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0647a12...5963fda. Read the comment docs.

@@ -53,6 +53,8 @@ const (
errNoIDGenerationScheme = "error: a recent breaking change means that an ID " +
"generation scheme is required in coordinator configuration settings. " +
"More information is available here: %s"

defaultQueryConversionCacheSize = 100
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably can be a lot higher?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, make it at least 4096

@@ -120,6 +122,9 @@ type Configuration struct {

// LookbackDuration determines the lookback duration for queries
LookbackDuration *time.Duration `yaml:"lookbackDuration"`

// Cache configurations.
Cache CacheConfigurations `yaml:"cache"`
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this instead of directly adding query conversion cache in case we have more cache options to add in the future

@@ -55,3 +55,7 @@ carbon:

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either add this to all configs or remove since it defaults

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd err towards default

// AddWithLock adds a value to the cache. Returns true if an eviction occurred.
func (c *QueryConversionLRU) AddWithLock(key string, value idx.Query) (evicted bool) {
// Check for existing item
c.Lock()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a fan of the pattern the hashicorp implementation uses for this--implement all the datastructure operations without a lock, and then wrap that class in a thin class with a lock. Saves you on defer overhead while still maintaining lock safety fairly easily. Thoughts?

}

// GetWithLock looks up a key's value from the cache.
func (c *QueryConversionLRU) GetWithLock(key string) (value idx.Query, ok bool) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If key is always a string, why is items map[interface]?


// QueryConversionLRU implements a fixed size LRU cache
type QueryConversionLRU struct {
sync.Mutex
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: embedding is weird here imo (sort of implies that the LRU is a lock); I would just use an explicit field.

c := &QueryConversionLRU{
size: size,
evictList: list.New(),
items: make(map[interface{}]*list.Element),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Size is known here, so let's use it in make

}

// AddWithLock adds a value to the cache. Returns true if an eviction occurred.
func (c *QueryConversionLRU) AddWithLock(key string, value idx.Query) (evicted bool) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I don't think the WithLock suffixes are really necessary; we can comment the class as thread safe or not instead.

@benraskin92 benraskin92 changed the title [WIP][DON'T REVIEW] Add lru cache for query conversion Add lru cache for query conversion Feb 22, 2019
@@ -53,6 +53,8 @@ const (
errNoIDGenerationScheme = "error: a recent breaking change means that an ID " +
"generation scheme is required in coordinator configuration settings. " +
"More information is available here: %s"

defaultQueryConversionCacheSize = 100
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, make it at least 4096

@@ -146,6 +151,37 @@ type FilterConfiguration struct {
CompleteTags Filter `yaml:"completeTags"`
}

// CacheConfigurations is the cache configurations.
type CacheConfigurations struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: CacheConfiguration

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better with the s as it implies it can be used for multiple cache configs.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All other configs are XyzConfiguration even with multiple configs, should probably stick to the precedent here

src/cmd/services/m3query/config/config.go Show resolved Hide resolved
src/query/storage/index.go Show resolved Hide resolved

"github.com/m3db/m3/src/dbnode/storage/index"
"github.com/m3db/m3/src/m3ninx/idx"
"github.com/m3db/m3/src/query/models"
"github.com/m3db/m3x/ident"
)

// QueryConvserionCache represents the query conversion LRU cache
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QueryConvserionCache -> QueryConversionCache

lru.Add("c", idx.NewTermQuery([]byte("bar"), []byte("foo")))
lru.Add("d", idx.NewTermQuery([]byte("baz"), []byte("biz")))
lru.Add("e", idx.NewTermQuery([]byte("qux"), []byte("quz")))
lru.Add("f", idx.NewTermQuery([]byte("quz"), []byte("qux")))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you check that the return types for the others are false and the return type for this one is true?

opts := m3db.NewOptions().
SetTagOptions(tagOptions).
SetLookbackDuration(lookbackDuration).
SetConsolidationFunc(consolidators.TakeLast)

conversionLRU, err := storage.NewQueryConversionLRU(conversionCacheSize)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would make a bit more sense to create the cache above and then pass the cache into the storage constructor?

return &m3storage{
clusters: clusters,
readWorkerPool: readWorkerPool,
writeWorkerPool: writeWorkerPool,
opts: opts,
nowFn: time.Now,
}
conversionCache: &storage.QueryConvserionCache{LRU: conversionLRU},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make a constructor for this, taking in the LRU as an argument?

// Optimization for single matcher case.
if len(matchers) == 1 {
q, err := matcherToQuery(matchers[0])
if err != nil {
return index.Query{}, err
}

cache.LRU.Add(string(k), q)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: rather than calling .LRU.Add(), can you put a thin wrapper Add function onto the query conversion cache?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason to do this?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reads better, more clarity; also if at some point we want to add more logic to the add function we can keep it in one place


"github.com/m3db/m3/src/dbnode/storage/index"
"github.com/m3db/m3/src/m3ninx/idx"
"github.com/m3db/m3/src/query/models"
"github.com/m3db/m3x/ident"
)

// QueryConvserionCache represents the query conversion LRU cache
type QueryConvserionCache struct {
mu sync.RWMutex
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're never using the Read lock, might be better to just make this a simple sync.Mutex

return &m3storage{
clusters: clusters,
readWorkerPool: readWorkerPool,
writeWorkerPool: writeWorkerPool,
opts: opts,
nowFn: time.Now,
}
// conversionCache: &storage.QueryConversionCache{LRU: conversionLRU},
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove

opts := m3db.NewOptions().
SetTagOptions(tagOptions).
SetLookbackDuration(lookbackDuration).
SetConsolidationFunc(consolidators.TakeLast)

// conversionLRU, err := storage.NewQueryConversionLRU(conversionCacheSize)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove


cache:
queryConversion:
size: 200
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this is very low

@@ -1,4 +1,4 @@
// Copyright (c) 2017 Uber Technologies, Inc.
// Copyright (c) 2019 Uber Technologies, Inc.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: revert

@benraskin92 benraskin92 force-pushed the braskin/lru_cache_query branch 2 times, most recently from 5963fda to 642cbd2 Compare February 25, 2019 19:50
Copy link
Collaborator

@arnikola arnikola left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of nits, and Validate() method on the configs

type QueryConversionCache struct {
mu sync.Mutex

LRU *QueryConversionLRU
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can make this private now

}

func TestQueryKey(t *testing.T) {
matchers := models.Matchers{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a few matchers here, maybe with different Types

require.False(t, ok)

// make sure "b" is still in the cache
_, ok = lru.Get([]byte("b"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: might as well check value here


// Validate validates the QueryConversionCacheConfiguration settings.
func (q *QueryConversionCacheConfiguration) Validate() error {
if *q.Size <= 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is 0 valid?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No

src/query/server/server.go Show resolved Hide resolved
src/query/storage/index_test.go Show resolved Hide resolved
src/query/storage/index_test.go Show resolved Hide resolved
Copy link
Collaborator

@arnikola arnikola left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved with a couple of nits


// Validate validates the QueryConversionCacheConfiguration settings.
func (q *QueryConversionCacheConfiguration) Validate() error {
switch {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be cleaner as

if q.Size != nil && q.Size <= 0 { // return error...

src/query/server/server.go Show resolved Hide resolved
// rewrite "e" and make sure nothing gets evicted
// since "e" is already in the cache.
evicted = lru.Set([]byte("e"), idx.NewTermQuery([]byte("qux"), []byte("quz")))
require.False(t, evicted)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: check that the value of e changes?

@@ -162,3 +162,13 @@ func TestTagOptionsConfig(t *testing.T) {
assert.Equal(t, []byte("foo"), opts.BucketName())
assert.Equal(t, models.TypePrependMeta, opts.IDSchemeType())
}

func TestNegativeQueryConversionSize(t *testing.T) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add test to validate nil case maybe? Up to you

@benraskin92 benraskin92 merged commit 8277c7a into master Feb 26, 2019
@arnikola arnikola deleted the braskin/lru_cache_query branch February 26, 2019 15:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants