diff --git a/cache.go b/cache.go index c32881a29..242e5a6ab 100644 --- a/cache.go +++ b/cache.go @@ -54,7 +54,7 @@ type Cache interface { SetStats(s StatsClient) } -// LRUCache represents a least recently used Cache implemenation. +// LRUCache represents a least recently used Cache implementation. type LRUCache struct { cache *lru.Cache counts map[uint64]uint64 @@ -483,3 +483,35 @@ func (s *SimpleCache) Fetch(id uint64) (*Bitmap, bool) { func (s *SimpleCache) Add(id uint64, b *Bitmap) { s.cache[id] = b } + +// NopCache represents a no-op Cache implementation. +type NopCache struct { + stats StatsClient +} + +// Ensure NopCache implements Cache. +var _ Cache = &NopCache{} + +// NewNopCache returns a new instance of NopCache. +func NewNopCache() *NopCache { + return &NopCache{ + stats: NopStatsClient, + } +} + +func (c *NopCache) Add(id uint64, n uint64) {} +func (c *NopCache) BulkAdd(id uint64, n uint64) {} +func (c *NopCache) Get(id uint64) uint64 { return 0 } +func (c *NopCache) IDs() []uint64 { return make([]uint64, 0, 0) } + +func (c *NopCache) Invalidate() {} +func (c *NopCache) Len() int { return 0 } +func (c *NopCache) Recalculate() { +} +func (c *NopCache) SetStats(s StatsClient) { + c.stats = s +} + +func (c *NopCache) Top() []BitmapPair { + return []BitmapPair{} +} diff --git a/fragment.go b/fragment.go index 64c31434b..b20dfd9c0 100644 --- a/fragment.go +++ b/fragment.go @@ -249,6 +249,8 @@ func (f *Fragment) openCache() error { f.cache = NewRankCache(f.CacheSize) case CacheTypeLRU: f.cache = NewLRUCache(f.CacheSize) + case CacheTypeNone: + f.cache = NewNopCache() default: return ErrInvalidCacheType } @@ -702,6 +704,10 @@ func (f *Fragment) Top(opt TopOptions) ([]Pair, error) { } func (f *Fragment) topBitmapPairs(rowIDs []uint64) []BitmapPair { + // Don't retrieve from storage if CacheTypeNone. + if f.CacheType == CacheTypeNone { + return f.cache.Top() + } // If no specific rows are requested, retrieve top rows. if len(rowIDs) == 0 { f.mu.Lock() diff --git a/fragment_test.go b/fragment_test.go index 01f4a46bc..982d4a066 100644 --- a/fragment_test.go +++ b/fragment_test.go @@ -415,6 +415,24 @@ func TestFragment_TopN_IDs(t *testing.T) { } } +// Ensure a fragment return none if CacheTypeNone is set +func TestFragment_TopN_NopCache(t *testing.T) { + f := test.MustOpenFragment("i", "f", pilosa.ViewStandard, 0, pilosa.CacheTypeNone) + defer f.Close() + + // Set bits on various rows. + f.MustSetBits(100, 1, 2, 3) + f.MustSetBits(101, 4, 5, 6, 7) + f.MustSetBits(102, 8, 9, 10, 11, 12) + + // Retrieve top rows. + if pairs, err := f.Top(pilosa.TopOptions{RowIDs: []uint64{100, 101, 200}}); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(pairs, []pilosa.Pair{}) { + t.Fatalf("unexpected pairs: %s", spew.Sdump(pairs)) + } +} + // Ensure the fragment cache limit works func TestFragment_TopN_CacheSize(t *testing.T) { slice := uint64(0) diff --git a/frame.go b/frame.go index 42990aeb6..d295ff01c 100644 --- a/frame.go +++ b/frame.go @@ -905,12 +905,13 @@ func (p importBitSet) Less(i, j int) bool { return p.rowIDs[i] < p.rowIDs[j] } const ( CacheTypeLRU = "lru" CacheTypeRanked = "ranked" + CacheTypeNone = "none" ) // IsValidCacheType returns true if v is a valid cache type. func IsValidCacheType(v string) bool { switch v { - case CacheTypeLRU, CacheTypeRanked: + case CacheTypeLRU, CacheTypeRanked, CacheTypeNone: return true default: return false diff --git a/index.go b/index.go index a5cdd1911..a07d28dfe 100644 --- a/index.go +++ b/index.go @@ -388,16 +388,12 @@ func (i *Index) createFrame(name string, opt FrameOptions) (*Frame, error) { } // Validate mutually exclusive options if ranges are enabled. - // - // NOTE(https://github.com/pilosa/pilosa/issues/399): - // Cache type should be validated as "none" once it is allowed. if opt.RangeEnabled { if opt.InverseEnabled { return nil, ErrInverseRangeNotAllowed - } else if opt.CacheType != "" && opt.CacheType != CacheTypeLRU { + } else if opt.CacheType != "" && opt.CacheType != CacheTypeNone { return nil, ErrRangeCacheNotAllowed } - opt.CacheSize = 0 } else { if len(opt.Fields) > 0 { return nil, ErrFrameFieldsNotAllowed diff --git a/index_test.go b/index_test.go index 2c23f7a40..0d17a72af 100644 --- a/index_test.go +++ b/index_test.go @@ -149,6 +149,18 @@ func TestIndex_CreateFrame(t *testing.T) { } }) + t.Run("RangeEnabledWithCacheTypeNone", func(t *testing.T) { + index := test.MustOpenIndex() + defer index.Close() + if _, err := index.CreateFrame("f", pilosa.FrameOptions{ + RangeEnabled: true, + CacheType: pilosa.CacheTypeNone, + CacheSize: uint32(5), + }); err != nil { + t.Fatal(err) + } + }) + t.Run("ErrFrameFieldsNotAllowed", func(t *testing.T) { index := test.MustOpenIndex() defer index.Close()