GithubHelp home page GithubHelp logo

dgraph-io / ristretto Goto Github PK

View Code? Open in Web Editor NEW
5.3K 5.3K 352.0 1.42 MB

A high performance memory-bound Go cache

Home Page: https://dgraph.io/blog/post/introducing-ristretto-high-perf-go-cache/

License: Apache License 2.0

Go 98.74% C 0.93% Assembly 0.34%
cache go golang library performance

ristretto's Issues

go get ristretto fails with error downloading bench/trace/ds1.arc.gz on windows

Ensure you do not have ristretto on your system and run go get -v github.com/dgraph-io/ristretto

$ go get -v github.com/dgraph-io/ristretto
go get: warning: modules disabled by GO111MODULE=auto in GOPATH/src;
        ignoring go.mod;
        see 'go help modules'
github.com/dgraph-io/ristretto (download)
# cd .; git clone https://github.com/dgraph-io/ristretto C:\Users\Ibrahim\go\src\github.com\dgraph-io\ristretto
Cloning into 'C:\Users\Ibrahim\go\src\github.com\dgraph-io\ristretto'...
Downloading bench/trace/ds1.arc.gz (7.4 MB)
Error downloading object: bench/trace/ds1.arc.gz (1c4eb88): Smudge error: Error downloading bench/trace/ds1.arc.gz (1c4eb8811b9840a6a006b5c6560e0a93e2dc978fae274378fb48ce2969e78366): batch response: This repository is over its data quota. Purchase more data packs to restore access.

Errors logged to C:\Users\Ibrahim\go\src\github.com\dgraph-io\ristretto\.git\lfs\logs\20190906T074835.9821484.log
Use `git lfs logs last` to view the log.
error: external filter 'git-lfs filter-process' failed
fatal: bench/trace/ds1.arc.gz: smudge filter lfs failed
warning: Clone succeeded, but checkout failed.
You can inspect what was checked out with 'git status'
and retry the checkout with 'git checkout -f HEAD'

package github.com/dgraph-io/ristretto: exit status 128

A similar failure was seen on https://ci.appveyor.com/project/manishrjain/badger/builds/27224437

Where are the releases?

I see you keep creating milestones and closing them, iterating the version. But where are the releases?

I'm sure you have your reasons, but would you be interested in doing git tags+github releases as well?

Add ability to specify custom hashing functions

Currently here https://github.com/dgraph-io/ristretto/blob/master/z/z.go#L20 all of the possible types are written out. However, users like me may want to use a "custom" struct as a key but that does not work at the moment since it is not written there and then my whole program panics.

I propose the following: add some kind of "advanced" option to the library which is a function (func (key interface{}) (uint64, error)) which accepts the key (interface{}) and produces a uint64 hash. It then could be called in the default case. If an error happens - only then we should panic.

What do you think?

Set up TeamCity

Migrate away from Github actions and use something else as discussed.

NewDefaultCache to encapsulate defaults

The current NewCache constructor requires the user to hard code ristretto defaults in their code.

Specifically, NumCounters is 100 x estimated items. The interface should allow the user to avoid hardcoding this into their code. Similarly, BufferItems: 64 is just a good number that works. The user should not need to hardcode this into client software either.

If any implementation changes in the future affect these currently valid defaults, the lack of encapsulation will require unnecessary changes to client code.

I suggest adding something like the following code (which also encapsulates the most likely default panic behavior on constructor errors). This allows the client to use defaults appropriate to their current version of ristretto. Also, it provides a simplified constructor that does not require client code knowledge of typically encapsulated implementation details.

As a small side benefit, the example code will also be cleaner and clearer.

// CountersPerItem is a good multiple of the number of expected items to track
const CountersPerItem = 100
// DefaultBufferItems is a good default size for Get buffers
const DefaultBufferItems = 64

func NewDefaultCache(expectedItems, avgCostPerItem int64) (cache *Cache) {
	
	cache, err := NewCache(&Config{
		NumCounters: expectedItems * CountersPerItem,
		MaxCost:     expectedItems * avgCostPerItem,
		BufferItems: DefaultBufferItems,  // number of keys per Get buffer.
	})
	if err != nil {
		panic(err)
	}
	return
} 

cmSketch not benefitting from four rows

I believe the commit f823dc4 broke the purpose of the cm sketch to have four rows. The bitwise-xor and mask are not creating the mix of indexes expected by the design. The same Increment/Estimate results, the same values, are achieved from a single row with or without the bitwise-xor. The earlier implementation seems to have been a good and very fast approximation to a distinct hash for each row except when the high 32 bits were all zero. One solution to fixing the earlier version could be to bitwise-or a single bit into the top 32 half. I can provide my testing and benchmarking on this offline if interested.

For small values of row length as used in the current unit tests, this doesn't matter. As the row length gets larger, the gap between the earlier algorithm and the current one widens and I believe becomes significant.

Add mechanism to clear entire cache.

I am adding ristretto to Dgraph but I am seeing a few issues that are caused because the cache is not being cleared after a DropAll operation.

I tried fixing this by recreating the entire cache every time that a DropAll op is processed, but this leads to out of memory issues. Ideally, ristretto has an API to efficiently clear the entire contents of the cache.

I'll take a look at the code and see if I can come up with something myself, but most likely I will need help from someone more familiar with the internals of ristretto.

Time aware LFU (windowing, temporary immunity)

Would you consider adding a time aware LFU, such that completely new entries can't have the probability of being evicted right after insertion?

Eg. New entries are given a 1 minute time frame to become useful. Potentially increasing survival rates of fresh entries.

Race condition in setBuf

==================
WARNING: DATA RACE
Read at 0x00c019349648 by goroutine 35:
  github.com/dgraph-io/ristretto.(*Cache).Set()
      /home/pawan0201/go/pkg/mod/github.com/dgraph-io/[email protected]/cache.go:205 +0x1ed
  github.com/dgraph-io/badger/v2/table.(*Table).block()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/table/table.go:459 +0xe9a
  github.com/dgraph-io/badger/v2/table.(*Iterator).next()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/table/iterator.go:296 +0x216
  github.com/dgraph-io/badger/v2/table.(*Iterator).next()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/table/iterator.go:311 +0x699
  github.com/dgraph-io/badger/v2/table.(*Iterator).Next()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/table/iterator.go:367 +0x70
  github.com/dgraph-io/badger/v2/table.(*ConcatIterator).Next()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/table/iterator.go:485 +0x59
  github.com/dgraph-io/badger/v2/table.(*node).next()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/table/merge_iterator.go:82 +0xce
  github.com/dgraph-io/badger/v2/table.(*MergeIterator).Next()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/table/merge_iterator.go:158 +0x6a
  github.com/dgraph-io/badger/v2/table.(*node).next()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/table/merge_iterator.go:80 +0x73
  github.com/dgraph-io/badger/v2/table.(*MergeIterator).Next()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/table/merge_iterator.go:158 +0x6a
  github.com/dgraph-io/badger/v2.(*Iterator).parseItem()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/iterator.go:648 +0x933
  github.com/dgraph-io/badger/v2.(*Iterator).Next()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/iterator.go:561 +0x1fd
  github.com/dgraph-io/badger/v2.TestDropAllWithPendingTxn.func3()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/managed_db_test.go:218 +0x183

Previous write at 0x00c019349648 by goroutine 82:
  github.com/dgraph-io/ristretto.(*Cache).Clear()
      /home/pawan0201/go/pkg/mod/github.com/dgraph-io/[email protected]/cache.go:254 +0x9f
  github.com/dgraph-io/badger/v2.(*DB).dropAll()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/db.go:1511 +0x57e
  github.com/dgraph-io/badger/v2.(*DB).DropAll()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/db.go:1467 +0x47
  github.com/dgraph-io/badger/v2.TestDropAllWithPendingTxn.func4()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/managed_db_test.go:248 +0x7f

Goroutine 35 (running) created at:
  github.com/dgraph-io/badger/v2.TestDropAllWithPendingTxn()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/managed_db_test.go:210 +0x3e6
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:909 +0x199

Goroutine 82 (running) created at:
  github.com/dgraph-io/badger/v2.TestDropAllWithPendingTxn()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/managed_db_test.go:245 +0x43f
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:909 +0x199
==================
badger 2020/02/05 10:11:51 INFO: Resuming writes
badger 2020/02/05 10:11:51 INFO: Got compaction priority: {level:0 score:1.73 dropPrefix:[]}
--- FAIL: TestDropAllWithPendingTxn (8.77s)
    managed_db_test.go:228: Got number of keys: 10000
    managed_db_test.go:224: Got error during value copy: Invalid value pointer offset: 3689714 greater than current offset: 20
    testing.go:853: race detected during execution of test

The line in question is

case c.setBuf <- i:
and

c.setBuf = make(chan *item, setBufSize)

https://teamcity.dgraph.io/viewLog.html?buildId=43004&buildTypeId=Badger_UnitTests revealed the race condition

Race condition between Cache#Set() and Cache#Del()

Thanks for the great write-up on this exciting project. Per a note in HN from one of the maintainers, I'm inquiring here about a possible race condition between Set() and Del().

As of this writing, Set() asynchronously enqueues the actual write, where as Del() seems to synchronously update the internal store. In other words, Del() does not appear to be aware of the write buffering implemented by Set().

At first glance, this would seem to introduce a race condition for invalidation. Wouldn't it be possible for the following sequence to be incorrect?

_ := c.Set(k, v, cost)
c.Del(k)
got, ok := c.Get(k)

I think most or all client code would expect read-your-write consistency here, but it does seem like Get() could return a valid value for k despite the invalidation on the prior line.

Memory leak when initializing multiple instances

I have the following test case that creates a lot of cache instances, and somehow the memory can not be garbage collected, and therefore used up a lot of memory.

func TestMemLeak(t *testing.T) {
	type state struct {
		cache *ristretto.Cache
	}

	t.Run("test_cache", func(t *testing.T) {
		for i := 0; i < 10; i++ {

			engines := make([]*state, 0)
			for e := 0; e < 100; e++ {
				cache, err := ristretto.NewCache(&ristretto.Config{
					NumCounters: 1e7, // 10x num items on full cache
					MaxCost:     1e6, // 1 MB total size
					BufferItems: 64,  // recommended value
				})
				require.Nil(t, err)
				cache.Close()

				engines = append(engines, &state{
                                        // setting the cache as nil will stop the memory leak
                                        // cache: nil, 
					cache: cache,
				})
			}
		}
	})
}

I measured the memory usage with /usr/bin/time like this:
time -l go test mem_leak_test.go -v -count=1 -run=TestMemLeak

Running the above test will use 8056582144 maximum resident set size, 8GB memory was used.

However, if I set cache: nil then there is no memory leak, 157466624 maximum resident set size, only 150MB was used.

Ristretto causing memory corruption

After adding Ristretto to our micro-service orchestrator written in go, we have been having data corruption issues. The cached data has randomly been inaccurate, forcing me to remove this in-memory cache implementation. Not sure how I would go about logging this information. I can try and take some time over the weekend to create an example repo to exhibit the issue.

Problems with the expiration

Question 1:

We know that the expired data is stored in the map's map, The key generation strategy is related to time.

like

time:0s storageBucket:1 cleanupBucket:0
time:1s storageBucket:1 cleanupBucket:0
time:2s storageBucket:1 cleanupBucket:0
time:3s storageBucket:1 cleanupBucket:0
time:4s storageBucket:1 cleanupBucket:0
time:5s storageBucket:2 cleanupBucket:1
time:6s storageBucket:2 cleanupBucket:1
time:7s storageBucket:2 cleanupBucket:1
time:8s storageBucket:2 cleanupBucket:1
time:9s storageBucket:2 cleanupBucket:1
time:10s storageBucket:3 cleanupBucket:2
time:11s storageBucket:3 cleanupBucket:2
time:12s storageBucket:3 cleanupBucket:2
time:13s storageBucket:3 cleanupBucket:2
time:14s storageBucket:3 cleanupBucket:2
time:15s storageBucket:4 cleanupBucket:3

If my cleanup time set is not good, will some records be skipped?

eg: Every 15s, only found bucket3. bucket1, bucket2 is never be cleaned up again, and alway in the expiration map, is it right?

Question2:

cleanup code as below

func (m *expirationMap) cleanup(store store, policy policy, onEvict onEvictFunc) {

	now := time.Now()
	bucketNum := cleanupBucket(now)
	keys := m.buckets[bucketNum]
	delete(m.buckets, bucketNum)
	
	for key, conflict := range keys {
		// if key is expired del it
		// bla bla
	}
}

assume now want to clean bucket1, bucket1 has 100 keys, 60 expired, 40 not, according to the code, 60 keys will be deleted, and bucket1 deleted
the other 40keys, will in store, but never be clean up, it doesn't matter? why design like this?
At the same time, I also noticed that when I get, I only check whether it is expired, and I did not delete it at this time, Wouldn't it be better to delete?

Question3:
cleanupTicker is check every two seconds, Checked the bucket about 5 seconds ago, not all keys expire within 5s(eg. 1Day), So I guess that most of the keys will not be cleaned, but bucket is deleled. So I wonder if this design can be improved?

Thank you very much, please forgive my english

more structured access to metrics.

Metrics returns a private type that doesn't show it's methods in the GoDoc. Ideally I'd like to be able to expose the metrics to a prometheus collector. It looks like I could possibly parse some string formatted output, but, ideally, I'd be able to get a current set of structuted mwtrics from the call.

[PR #122] It's not possible to reuse a key after TTL eviction

Hello,

I've found an issue when playing with the new TTL feature.
It's not possible to reuse a key to update the cache with a new value.

The sample use case :

c, err := NewCache(...)

// Add a value with key 1 in the cache
c.SetWithTTL(1, 1, 1, 5*time.Second)
_, ok := c.Get(1) // return true

// some time later
// Add another value with key 1 in the cache
c.SetWithTTL(1, 1, 1, 5*time.Second)
_, ok := c.Get(1) // return false

I submit the following PR to correct this issue : #130

Race in cache.clear

The following race condition was seen in badger on PR dgraph-io/badger#1066

------- Stdout: -------
=== RUN   TestDropAllWithPendingTxn
badger 2019/10/21 08:34:34 INFO: All 0 tables opened in 0s
badger 2019/10/21 08:34:35 DEBUG: Flushing memtable, mt.size=34867 size of flushChan: 0
badger 2019/10/21 08:34:35 DEBUG: Storing value log head: {Fid:0 Len:30 Offset:4831937}
badger 2019/10/21 08:34:35 DEBUG: Storing value log head: {Fid:1 Len:30 Offset:4026620}
badger 2019/10/21 08:34:35 DEBUG: Flushing memtable, mt.size=34837 size of flushChan: 0
badger 2019/10/21 08:34:35 DEBUG: Storing value log head: {Fid:2 Len:30 Offset:3221294}
badger 2019/10/21 08:34:35 DEBUG: Flushing memtable, mt.size=34825 size of flushChan: 0
badger 2019/10/21 08:34:36 DEBUG: Storing value log head: {Fid:3 Len:30 Offset:2415968}
badger 2019/10/21 08:34:36 DEBUG: Flushing memtable, mt.size=34777 size of flushChan: 0
badger 2019/10/21 08:34:36 DEBUG: Storing value log head: {Fid:4 Len:30 Offset:1610642}
badger 2019/10/21 08:34:36 DEBUG: Flushing memtable, mt.size=34757 size of flushChan: 0
badger 2019/10/21 08:34:36 DEBUG: Storing value log head: {Fid:5 Len:30 Offset:805316}
badger 2019/10/21 08:34:36 DEBUG: Flushing memtable, mt.size=34753 size of flushChan: 0
badger 2019/10/21 08:34:36 INFO: Got compaction priority: {level:0 score:1.2 dropPrefix:[]}
badger 2019/10/21 08:34:36 INFO: Running for level: 0
badger 2019/10/21 08:34:36 DEBUG: LOG Compact. Added 3534 keys. Skipped 0 keys. Iteration took: 47.099272ms
badger 2019/10/21 08:34:36 DEBUG: Discard stats: map[]
badger 2019/10/21 08:34:36 INFO: LOG Compact 0->1, del 6 tables, add 1 tables, took 64.409113ms
badger 2019/10/21 08:34:36 INFO: Compaction for level: 0 DONE
badger 2019/10/21 08:34:37 DEBUG: Flushing memtable, mt.size=34895 size of flushChan: 0
badger 2019/10/21 08:34:37 DEBUG: Storing value log head: {Fid:5 Len:30 Offset:5637272}
badger 2019/10/21 08:34:37 DEBUG: Flushing memtable, mt.size=34775 size of flushChan: 0
badger 2019/10/21 08:34:37 DEBUG: Storing value log head: {Fid:6 Len:30 Offset:4831946}
badger 2019/10/21 08:34:37 DEBUG: Storing value log head: {Fid:7 Len:31 Offset:4026628}
badger 2019/10/21 08:34:37 DEBUG: Flushing memtable, mt.size=34753 size of flushChan: 0
badger 2019/10/21 08:34:38 DEBUG: Flushing memtable, mt.size=29077 size of flushChan: 0
badger 2019/10/21 08:34:38 DEBUG: Storing value log head: {Fid:8 Len:31 Offset:2415973}
badger 2019/10/21 08:34:38 DEBUG: Storing value log head: {Fid:9 Len:31 Offset:1610645}
badger 2019/10/21 08:34:38 DEBUG: Flushing memtable, mt.size=34745 size of flushChan: 0
badger 2019/10/21 08:34:38 DEBUG: Storing value log head: {Fid:10 Len:31 Offset:805317}
badger 2019/10/21 08:34:38 DEBUG: Flushing memtable, mt.size=34945 size of flushChan: 0
badger 2019/10/21 08:34:38 INFO: Got compaction priority: {level:0 score:1.2 dropPrefix:[]}
badger 2019/10/21 08:34:38 INFO: Running for level: 0
badger 2019/10/21 08:34:38 DEBUG: LOG Compact. Added 3603 keys. Skipped 0 keys. Iteration took: 40.49591ms
badger 2019/10/21 08:34:38 DEBUG: LOG Compact. Added 3367 keys. Skipped 0 keys. Iteration took: 29.324284ms
badger 2019/10/21 08:34:38 DEBUG: Discard stats: map[]
badger 2019/10/21 08:34:38 INFO: LOG Compact 0->1, del 7 tables, add 2 tables, took 84.556191ms
badger 2019/10/21 08:34:38 INFO: Compaction for level: 0 DONE
badger 2019/10/21 08:34:39 DEBUG: Flushing memtable, mt.size=34643 size of flushChan: 0
badger 2019/10/21 08:34:39 DEBUG: Storing value log head: {Fid:10 Len:31 Offset:5637285}
badger 2019/10/21 08:34:39 DEBUG: Flushing memtable, mt.size=34795 size of flushChan: 0
badger 2019/10/21 08:34:39 DEBUG: Storing value log head: {Fid:11 Len:31 Offset:4831957}
badger 2019/10/21 08:34:39 DEBUG: Flushing memtable, mt.size=34803 size of flushChan: 0
badger 2019/10/21 08:34:39 DEBUG: Storing value log head: {Fid:12 Len:31 Offset:4026629}
badger 2019/10/21 08:34:40 DEBUG: Storing value log head: {Fid:13 Len:31 Offset:3221301}
badger 2019/10/21 08:34:40 DEBUG: Flushing memtable, mt.size=34793 size of flushChan: 0
badger 2019/10/21 08:34:40 DEBUG: Storing value log head: {Fid:14 Len:31 Offset:2415973}
badger 2019/10/21 08:34:40 DEBUG: Flushing memtable, mt.size=34805 size of flushChan: 0
badger 2019/10/21 08:34:40 INFO: Got compaction priority: {level:0 score:1 dropPrefix:[]}
badger 2019/10/21 08:34:40 INFO: Running for level: 0
badger 2019/10/21 08:34:40 DEBUG: LOG Compact. Added 3603 keys. Skipped 0 keys. Iteration took: 44.412168ms
badger 2019/10/21 08:34:40 DEBUG: LOG Compact. Added 3607 keys. Skipped 0 keys. Iteration took: 39.613965ms
badger 2019/10/21 08:34:40 DEBUG: LOG Compact. Added 2705 keys. Skipped 0 keys. Iteration took: 28.006555ms
badger 2019/10/21 08:34:40 DEBUG: Discard stats: map[]
badger 2019/10/21 08:34:40 INFO: LOG Compact 0->1, del 7 tables, add 3 tables, took 127.699034ms
badger 2019/10/21 08:34:40 INFO: Compaction for level: 0 DONE
badger 2019/10/21 08:34:43 INFO: DropAll called. Blocking writes...
badger 2019/10/21 08:34:43 INFO: Writes flushed. Stopping compactions now...
badger 2019/10/21 08:34:43 INFO: Deleted 3 SSTables. Now deleting value logs...
badger 2019/10/21 08:34:43 INFO: Value logs deleted. Creating value log file: 0
badger 2019/10/21 08:34:43 INFO: Deleted 15 value log files. DropAll done.
==================
WARNING: DATA RACE
Write at 0x00c174a2fc50 by goroutine 40:
  github.com/dgraph-io/ristretto.(*Cache).collectMetrics()
      /home/pawan0201/go/src/github.com/dgraph-io/ristretto/cache.go:287 +0x55
  github.com/dgraph-io/ristretto.(*Cache).Clear()
      /home/pawan0201/go/src/github.com/dgraph-io/ristretto/cache.go:241 +0x14e
  github.com/dgraph-io/badger.(*DB).dropAll()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/db.go:1472 +0x556
  github.com/dgraph-io/badger.(*DB).DropAll()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/db.go:1428 +0x44
  github.com/dgraph-io/badger.TestDropAllWithPendingTxn.func4()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/managed_db_test.go:236 +0x6f

Previous read at 0x00c174a2fc50 by goroutine 253:
  github.com/dgraph-io/ristretto.(*Cache).Get()
      /home/pawan0201/go/src/github.com/dgraph-io/ristretto/cache.go:167 +0x103
  github.com/dgraph-io/badger/table.(*Table).block()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/table/table.go:364 +0x164c
  github.com/dgraph-io/badger/table.(*Iterator).next()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/table/iterator.go:296 +0x216
  github.com/dgraph-io/badger/table.(*Iterator).next()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/table/iterator.go:311 +0x6a6
  github.com/dgraph-io/badger/table.(*Iterator).Next()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/table/iterator.go:367 +0x70
  github.com/dgraph-io/badger/table.(*ConcatIterator).Next()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/table/iterator.go:485 +0x59
  github.com/dgraph-io/badger/y.(*MergeIterator).Next()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/y/iterator.go:216 +0xca
  github.com/dgraph-io/badger.(*Iterator).parseItem()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/iterator.go:647 +0x98e
  github.com/dgraph-io/badger.(*Iterator).Next()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/iterator.go:560 +0x20c
  github.com/dgraph-io/badger.TestDropAllWithPendingTxn.func3()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/managed_db_test.go:206 +0x157

Goroutine 40 (running) created at:
  github.com/dgraph-io/badger.TestDropAllWithPendingTxn()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/managed_db_test.go:233 +0x428
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:865 +0x163

Goroutine 253 (running) created at:
  github.com/dgraph-io/badger.TestDropAllWithPendingTxn()
      /home/pawan0201/go/src/github.com/dgraph-io/badger/managed_db_test.go:198 +0x3cf
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:865 +0x163
==================
badger 2019/10/21 08:34:43 INFO: Resuming writes
badger 2019/10/21 08:34:44 INFO: Got compaction priority: {level:0 score:1.73 dropPrefix:[]}
--- FAIL: TestDropAllWithPendingTxn (9.40s)
    managed_db_test.go:216: Got number of keys: 10000
    managed_db_test.go:216: Got number of keys: 10000
    managed_db_test.go:220: Got error during key lookup: Key not found
    testing.go:809: race detected during execution of test

https://teamcity.dgraph.io/viewLog.html?buildId=29213&buildTypeId=Badger_UnitTests&tab=buildResultsDiv

Why is ristretto using so much memory?

package main

import (
	"fmt"
	"math/rand"
	"strconv"

	"github.com/dgraph-io/ristretto"
)

const (
	maxCnt  = 1e7
	maxLoop = 1e8
)

func main() {
	cahchePool, err := ristretto.NewCache(&ristretto.Config{
		NumCounters: 1 * 1e7,
		MaxCost:     200 * (1 << 20), // allocate 200M, but running it need 2GB, why?(when i run this program, it kill by OOM)
		BufferItems: 64,
		Metrics:     true,
	})

	if err != nil {
		panic(err)
	}

	key := "def48b5abb6388d3cbbb18e550f47b4cYB6eRI3cK1VN2qCEHp8kvuMuH20dq10cYDcG2e"
	for i := 0; i < maxLoop; i++ {
		suffix := strconv.Itoa(rand.Intn(maxCnt))
		cahchePool.Set(key+suffix, suffix, int64(len(key+suffix)))
		if (i % maxCnt) == 0 {
			fmt.Println(cahchePool.Metrics)
		}
	}

	cnt := 0
	for i := 0; i < maxCnt; i++ {
		if _, found := cahchePool.Get(key + strconv.Itoa(i)); found {
			cnt++
		}
	}
	fmt.Println("cnt:", cnt, "\n", cahchePool.Metrics)
}

MaxCost sometimes ignored

I have written a simple program that Gets and Sets entries in Ristretto. The program sets a MaxCost (in bytes) and passes a value for the cost to Set based on the size in bytes of the value.

Code can be found here: https://gist.github.com/erikdubbelboer/bd904c51f00079421fd3eb2a061a50c0

As you can see from the source Ristretto should be limited to 1GB of memory. The program doesn't do anything strange.

In most cases this program works fine and Ristretto limits the memory usage correctly. But in the case of the exact code as above the memory usage keeps growing.

Also keep in mind that runtime.GC() is run every second so it's not just uncollected allocations we seeing here. Not running runtime.GC() has the same result just more variation in the amount allocated.

This is the output when run for a couple of seconds (just go run ristretto-bug.go):

time: 1s alloc: 1033.72 MiB hits: 43 misses: 2614
time: 2s alloc: 1035.74 MiB hits: 71 misses: 3230
time: 3s alloc: 1038.76 MiB hits: 98 misses: 3210
time: 4s alloc: 1048.77 MiB hits: 87 misses: 3236
time: 5s alloc: 1065.79 MiB hits: 109 misses: 3229
...
time: 45s alloc: 5893.52 MiB hits: 642 misses: 2773
time: 46s alloc: 6049.54 MiB hits: 704 misses: 2689

The output of http://localhost:6060/debug/pprof/heap?debug=1 shows that all memory is allocated inside strings.Repeat.

My guess is that in some cases Ristretto keeps references to memory for entries that aren't part of the cache anymore.

[Question] over time can TinyLFU make it more difficult to add cache entries?

It sounded to me that if the value calculated by new entries is higher than the lowest entry in a random sample, the new entry gets admitted. Otherwise not.

What I wonder is if it the value is calculated based off on the frequency from LFU. If so, won't the number of discarded items go up over time? Imagine if there are only reads for a minute or 10minutes, the frequency of most items increase and they gain a higher value than any new incoming entries.

I'm not confident in how the value is calculated, so I apologies if this is way off. Also curious if you have metrics for discarded (new items that was never added) metrics?

Documented Behaviour of NumCounters Not Observed

Currently it is stated that

// For example, if you expect your cache to hold 1,000,000 items when full,
// NumCounters should be 10,000,000 (10x). Each counter takes up 4 bits, so
// keeping 10,000,000 counters would require 5MB of memory.

over here. However this behaviour isnt correct when utlising the cache. When utilising the cache with the following config

cache, err := ristretto.NewCache(&ristretto.Config{
		NumCounters: 10000,           // number of keys to track frequency of (10000).
		MaxCost:     1000, // maximum cost of cache (1000 objects).
		BufferItems: 64,             // number of keys per Get buffer.
		Cost: func(value interface{})int64{
			return 1
		},
	})

After the cache is filled with the first 1000 objects and is at its maximum capacity, any new items cannot be added into it. All my following Set fail. None of the old items currently in the cache are evicted to make way for new ones. This is however resolved if the NumCounters is set the same as the maximum size of the cache(1000 objects).

cache, err := ristretto.NewCache(&ristretto.Config{
		NumCounters: 1000,           // number of keys to track frequency of (1000).
		MaxCost:     1000, // maximum cost of cache (1000 objects).
		BufferItems: 64,             // number of keys per Get buffer.
		Cost: func(value interface{})int64{
			return 1
		},
	})

The above allowed the cache to work as normal, with majority of my Sets succeeding and my hit ratio staying at 0.94 instead of rapidly dropping. Is there anything else that needs to be done to get the cache working with 10x the number of keys or is this a gap in the documentation ?

TestRingLossy fails with drain error

TestRingLossy fails on master with drain error

Run

while go clean -testcache && go test -v -race -run='TestRingLossy' -timeout=1m; do :; done    

to reproduce the failure

=== RUN   TestRingLossy
--- FAIL: TestRingLossy (0.01s)
    ring_test.go:57: drain error
FAIL
exit status 1
FAIL	github.com/dgraph-io/ristretto	0.012s

ristretto/ring_test.go

Lines 41 to 59 in ae326c3

func TestRingLossy(t *testing.T) {
drainCount := 0
buffer := newRingBuffer(ringLossy, &ringConfig{
Consumer: &TestConsumer{
push: func(items []uint64) {
drainCount++
},
},
Capacity: 4,
})
buffer.Push(1)
buffer.Push(2)
buffer.Push(3)
buffer.Push(4)
time.Sleep(5 * time.Millisecond)
if drainCount == 0 {
t.Fatal("drain error")
}
}

`bench` fails to build

# github.com/dgraph-io/ristretto/bench
src/github.com/dgraph-io/ristretto/bench/bench.go:325:10: undefined: ristretto.PolicyLog

Found by gopkgs.io

Snapshot of current cache items

Hello there ๐Ÿ˜„ thanks for developing ristretto!
This is more of a general question rather than a issue: is there a way of creating a persistent snapshot for a running cache?
I've been trying with gob.NewEncoder(buffer).Encode(cache) but I have an error gob: type ristretto.Metrics has no exported fields which is (apparently) the intended behaviour of gob package (reference).
Any ideas if this is possible?
Thanks

keyhash and conflict all same

assume Key A, Key B

A(keyHash) A(Confilct) equal B(keyHash) B(Confilct)

In this case, do I have to double check the value every time?

Thanks

Add ability to hook into the "eviction" event

Please provide another member onEvict in the Config struct which would be a function that would be called on eviction if it is present. This is needed so that users would be able to do some more, extra house-keeping from their side.

Please publish a Release

The current and only release is old. Go get will fetch this old version by default, without the TTL functionality. Go get will thus fetch a version that does not comply with the README.

Can you publish a release that matches the documentation?
Thank you.

Run tests on Go 1.13

The ci pipeline introduced in #48 run tests only on go 1.12

Tests should run on latest stable version (1.13) also.

When using `string` as keys, there could be hash collisions, and thus `Get` could return the wrong data

This is more a "design question" than an actual "issue", however given its implications I think it is an important question which impacts either the design or the usage constraints.

Given that the KeyToHash (1) function supports string (and []byte), and it returns uint64, it is possible that there are hash collisions.

However the cache.Get (2) method doesn't check if the found entry (if any) actually has the given key. (In fact the store doesn't even support storing the key.)

(1) https://github.com/dgraph-io/ristretto/blob/master/z/z.go#L20
(2) https://github.com/dgraph-io/ristretto/blob/master/cache.go#L83

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.