Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Goキャッシュライブラリgolang-lruをLet Read and Understand!!

Goキャッシュライブラリgolang-lruをLet Read and Understand!!

Tomohiko Tanihata

June 14, 2024
Tweet

Other Decks in Programming

Transcript

  1. ϚΠΫϩαʔϏεؒ௨৴ʹ͓͚ΔΩϟογϡ - enechainͰ͸ϚΠΫϩαʔϏεΞʔΩςΫνϟΛ࠾༻ - σʔλΞΫηεͰҰ෦ΩϟογϡΛར༻ - golang-lruΛ࢖༻͍ͯ͠Δ 4 Microservice A

    Microservice B Microservice B Microservice C Ωϟογϡར༻ ར༻৚݅ - සൟʹΞΫηε͞ΕΔ - มߋස౓͕௿͍ - σʔλͷҰ؏ੑ͕ݫີʹෆཁ
  2. LRU vs LFU LRUʢLeast Recently Usedʣ ࠷ۙΞΫηε͞Ε͍ͯͳ͍ΞΠςϜ͔Β࡟আ 😁 ࣌ؒతہॴੑʹڧ͍ 😢

    සൟʹ࢖༻͞ΕΔ͕࠷ۙ࢖༻͞Ε͍ͯͳ͍ΞΠςϜ͕࡟আ LFUʢLeast Frequently Usedʣ සൟʹΞΫηε͞Ε͍ͯͳ͍ΞΠςϜ͔Β࡟আ 😁 සൟʹΞΫηε͞ΕΔΞΠςϜΛอ࣋ɺߴස౓ΞΫηεʹڧ͍ 😢 ୹ظؒʹଟ͘ΞΫηε͞Ε͕ͨ௕ظతʹ͸࢖ΘΕͳ͍ΞΠςϜ͕อ࣋ 6
  3. LRU vs LFU 7 key ௚ۙͷࢀর࣌ؒ ࢀরճ਺ 1 10:00:00 5

    2 11:00:00 3 3 12:00:00 1 4 13:00:00 2 5 14:00:00 1 ௥Ճ ࡟আର৅ʢLRUʣ ࡟আର৅ʢLFUʣ
  4. golang-lruͷػೳҰཡ 9 - Simple LRUʢ ୯७ͳLRUΩϟογϡ - Expirable LRU ʢLRU

    + TTLʣ LRUʹՃ͑ͯɺΞΠςϜ͕ࢦఆ͞Εͨ࣌ؒܦաޙʹظݶ੾ΕʹͳΔػೳΛ࣋ͭ - TwoQueueCacheʢLRU + LFUʣ ࠷ۙ࢖༻͞ΕͨΤϯτϦͱසൟʹ࢖༻͞ΕΔΤϯτϦΛผʑʹ௥੻ - Adaptive Replacement Cacheʢ LRUͱLFUͷ྆ํͷϝϦοτΛऔΓೖΕ
  5. - Simple LRUʢ ୯७ͳLRUΩϟογϡ - Expirable LRU ʢLRU + TTLʣ

    LRUʹՃ͑ͯɺΞΠςϜ͕ࢦఆ͞Εͨ࣌ؒܦաޙʹظݶ੾ΕʹͳΔػೳΛ࣋ͭ - TwoQueueCacheʢLRU + LFUʣ ࠷ۙ࢖༻͞ΕͨΤϯτϦͱසൟʹ࢖༻͞ΕΔΤϯτϦΛผʑʹ௥੻ - Adaptive Replacement Cacheʢ LRUͱLFUͷ྆ํͷϝϦοτΛऔΓೖΕ golang-lruͷػೳҰཡ 10 ͜Εͷ࿩
  6. Simple LRUͷΞϧΰϦζϜ 12 prev value next prev value next prev

    value next ૒ํ޲॥؀ϦετΛར༻ͯ͠ॱ൪؅ཧ - ઌ಄ͷprevΛ຤ඌʹ࿈݁ - ຤ඌͷnextΛઌ಄ʹ࿈݁ - Ϧετͷ຤ඌΛࢀর͍ͨ࣌͠ʹઌ಄ͷprevΛݟΕ͹Α͍ Entry
  7. อଘEntry Simple LRUͷΞϧΰϦζϜ 14 entAΛ௥Ճ root entA อଘEntry - entA

    entA.prev = root entA.next = root root.next = entA root.prev = entA Size 4 rootͷྡʹentAΛૠೖ
  8. อଘEntry entB Simple LRUͷΞϧΰϦζϜ 15 entBΛ௥Ճ root entA อଘEntry -

    entA - entB Size 4 entB.prev = root entB.next = entA root.next = entB entA.prev = entB rootͱentAͷؒʹentBΛૠೖ
  9. อଘEntry entC entB Simple LRUͷΞϧΰϦζϜ 16 entCΛ௥Ճ root entA อଘEntry

    - entA - entB - entC Size 4 entC.prev = root entC.next = entB root.next = entC entB.prev = entC rootͱentBͷؒʹentAΛૠೖ
  10. อଘEntry อଘEntry - entA - entB - entC - entD

    entD entC entB Simple LRUͷΞϧΰϦζϜ 17 entDΛ௥Ճ root entA Size 4 entD.prev = root entD.next = entC root.next = entD entC.prev = entD rootͱentCͷؒʹentDΛૠೖ
  11. อଘEntry entE entD entC entB Simple LRUͷΞϧΰϦζϜ 18 entEΛ௥Ճ root

    อଘEntry - entB - entC - entD - entE Size 4 entE.prev = root entE.next = entD root.next = entE entD.prev = entE rootͱentDͷؒʹentEΛૠೖ entA͸👋 🗑
  12. อଘEntry entC entE entD entB Simple LRUͷΞϧΰϦζϜ 19 entCΛ௥Ճ root

    อଘEntry - entB - entC - entD - entE Size 4 entE.next = entD entB.prev = entD entC.prev = root entC.next = entE root.next = entC entE.prev = entC rootͱentEͷؒʹentCΛҠಈ อଘEntry͸มΘΒͣ
  13. Simple LRUͷ಺෦࣮૷ΛಡΉ αϯϓϧίʔυ 20 func main() { l, _ :=

    lru.New[int, any](128) for i := 0; i < 256; i++ { l.Add(i, nil) } if l.Len() != 128 { panic(fmt.Sprintf("bad len: %v", l.Len())) } } sizeҰகΛ֬ೝ
  14. Simple LRUͷ಺෦࣮૷ΛಡΉ LRU structΛݟͯΈΔ 21 // LRU implements a non-thread

    safe fixed size LRU cache type LRU[K comparable, V any] struct { size int evictList *internal.LruList[K, V] items map[K]*internal.Entry[K, V] onEvict EvictCallback[K, V] } ૒ํ޲॥؀Ϧετ KVΛอ࣋͢ΔMap
  15. Simple LRUͷ಺෦࣮૷ΛಡΉ AddΛݟͯΈΔ: طʹListʹitem͕ଘࡏ͍ͯ͠Δ৔߹ 22 func (c *LRU[K, V]) Add(key

    K, value V) (evicted bool) { // Check for existing item if ent, ok := c.items[key]; ok { c.evictList.MoveToFront(ent) ent.Value = value return false } // Add new item … } Mapʹ஋͕͋Δ͔֬ೝ Listͷઌ಄ʹҠಈ
  16. Simple LRUͷ಺෦࣮૷ΛಡΉ AddΛݟͯΈΔ: Listʹitem͕ଘࡏ͠ͳ͍৔߹ 23 func (c *LRU[K, V]) Add(key

    K, value V) (evicted bool) { // Check for existing item … // Add new item ent := c.evictList.PushFront(key, value) c.items[key] = ent // Verify size not exceeded if c.evictList.Length() > c.size { c.removeOldest() } return evict } Listͷઌ಄ʹૠೖ Listͷ຤ඌΛ࡟আ
  17. Expirable LRUͷΞϧΰϦζϜ Simple LRUʹTTLͷ੍ޚΛ௥Ճ - TTLʢTime To Liveʣ - Entryͷੜଘ࣌ؒʢ࣍ͷྫͰ͸3sʣ

    - Entry͸TTLܦաޙʹBucket୯ҐͰ࡟আ - Bucketʢόέπʣ - ࡟আ͢ΔEntryΛ؅ཧ͢Δശ - BucketΛnݸ༻ҙ͢Δ৔߹ɺTTLͰnճ࡟আʢ࣍ͷྫͰ͸3ݸɻ΄Μ·͸100ݸʣ 26
  18. Expirable LRUͷΞϧΰϦζϜ ʢA, B, B, CʣͷॱʹAdd͞Εͨ৔߹ 27 Next
 Cleanup Bucket

    Add Bucket Time 0 2 1 0 2 TTL 4 5 1 0 2 A Entry B Buckets ܦա࣌ؒ0.0s B C TTL͸3s Bucket͸3ݸ ܦա࣌ؒ s
  19. Expirable LRUͷΞϧΰϦζϜ ʢA, B, B, CʣͷॱʹAdd͞Εͨ৔߹ 28 Next
 Cleanup Bucket

    Add Bucket Time 0 2 1 0 2 TTL 4 5 1 0 2 A Entry B Buckets A B Delete 1 0 ܦա࣌ؒ1.0s B C A, B͕Bucket2ʹ௥Ճ ܦա࣌ؒ s
  20. Expirable LRUͷΞϧΰϦζϜ ʢA, B, B, CʣͷॱʹAdd͞Εͨ৔߹ 29 Next
 Cleanup Bucket

    Add Bucket Time 0 2 1 0 2 TTL 4 5 1 0 2 A Entry B Buckets A B 1 0 Delete 2 1 ܦա࣌ؒ2.0s B C C B͸Bucket0ʹҠಈ ܦա࣌ؒ s
  21. Expirable LRUͷΞϧΰϦζϜ ʢA, B, B, CʣͷॱʹAdd͞Εͨ৔߹ 30 Next
 Cleanup Bucket

    Add Bucket Time 0 2 1 0 2 TTL 4 5 1 0 2 A Entry B Buckets 1 0 2 1 A ܦա࣌ؒ3.0s B C B C Bucket2ͷEntryͷTTL͕ܦա͢Δ·Ͱ଴ͭ ܦա࣌ؒ s
  22. Expirable LRUͷΞϧΰϦζϜ ʢA, B, B, CʣͷॱʹAdd͞Εͨ৔߹ 31 Next
 Cleanup Bucket

    Add Bucket Time 0 2 1 0 2 TTL 4 5 1 0 2 A Entry B Buckets ܦա࣌ؒ3.4s 1 0 2 1 Delete B C B C A͸࡟আ 0.4sԆ௕ 0 2 ܦա࣌ؒ s
  23. Expirable LRUͷΞϧΰϦζϜ ʢA, B, B, CʣͷॱʹAdd͞Εͨ৔߹ 32 Next
 Cleanup Bucket

    Add Bucket Time 0 2 1 0 2 TTL 4 5 1 0 2 A Entry B Buckets ܦա࣌ؒ4.6s 1 0 2 1 Delete B C 0 2 B, C͸࡟আ ஗͍ํʹ߹Θͤͯ0.6s B͸TTL௒͑ͯ΋ গ͠ͷ͚ؒͩ Bucketʹ࢒Δ ܦա࣌ؒ s
  24. Expirable LRUͷ಺෦࣮૷ΛಡΉ αϯϓϧίʔυ 33 func main() { // make cache

    with 10ms TTL and 5 max keys cache := expirable.NewLRU[string, string](5, nil, time.Millisecond*10) // set value under key1. cache.Add("key1", "val1") // wait for cache to expire time.Sleep(time.Millisecond * 12) // get value under key1 after key expiration r, ok = cache.Get("key1") fmt.Printf("value after expiration is found: %v, value: %q\n", ok, r) } TTLΛ௒͑ΔͷΛ଴ͭ Entry͕࡟আ͞Ε͍ͯΔ
  25. Expirable LRUͷ಺෦࣮૷ΛಡΉ 34 LRU structΛݟͯΈΔ type LRU[K comparable, V any]

    struct { size int evictList *internal.LruList[K, V] items map[K]*internal.Entry[K, V] onEvict EvictCallback[K, V] // expirable options mu sync.Mutex ttl time.Duration done chan struct{} // buckets for expiration buckets []bucket[K, V] // uint8 because it's number between 0 and numBuckets nextCleanupBucket uint8 } Simple LRUͱಉ͡ TTL͕ఆٛ Bucket͕ఆٛ
  26. Expirable LRUͷ಺෦࣮૷ΛಡΉ 35 AddΛݟͯΈΔ: طʹListʹitem͕ଘࡏ͍ͯ͠Δ৔߹ func (c *LRU[K, V]) Add(key

    K, value V) (evicted bool) { c.mu.Lock() defer c.mu.Unlock() now := time.Now() // Check for existing item if ent, ok := c.items[key]; ok { c.evictList.MoveToFront(ent) c.removeFromBucket(ent) ent.Value = value ent.ExpiresAt = now.Add(c.ttl) c.addToBucket(ent) return false } // Add new item … } Mapʹ஋͕͋Δ͔֬ೝ Bucketʹ௥Ճ Bucket͔Β࡟আ
  27. Expirable LRUͷ಺෦࣮૷ΛಡΉ 36 AddΛݟͯΈΔ: Listʹitem͕ଘࡏ͠ͳ͍৔߹ func (c *LRU[K, V]) Add(key

    K, value V) (evicted bool) { // Check for existing item … // Add new item ent := c.evictList.PushFrontExpirable(key, value, now.Add(c.ttl)) c.items[key] = ent c.addToBucket(ent) evict := c.size > 0 && c.evictList.Length() > c.size // Verify size not exceeded if evict { c.removeOldest() } return evict } Size௒͑ͯͨΒListͷ຤ඌΛ࡟আ Listͷઌ಄ʹૠೖ Bucketʹ௥Ճ
  28. Expirable LRUͷ಺෦࣮૷ΛಡΉ 37 BucketsͰͷ࡟আΛ௥ͬͯΈΔ: NewLRUฤ func NewLRU(size int, onEvict EvictCallback,

    ttl time.Duration) *LRU { … if res.ttl != noEvictionTTL { go func(done <-chan struct{}) { ticker := time.NewTicker(res.ttl / numBuckets) defer ticker.Stop() for { select { case <-done: return case <-ticker.C: res.deleteExpired() } } }(res.done) } return &res } TTL / numBucketsඵ͝ͱʹൃՐ goroutine্ཱͪ͛ͯtickerىಈ
  29. Expirable LRUͷ಺෦࣮૷ΛಡΉ 38 BucketsͰͷ࡟আΛ௥ͬͯΈΔ: deleteExpiredฤ func (c *LRU[K, V]) deleteExpired()

    { c.mu.Lock() bucketIdx := c.nextCleanupBucket timeToExpire := time.Until(c.buckets[bucketIdx].newestEntry) // wait for newest entry to expire before cleanup without holding lock if timeToExpire > 0 { c.mu.Unlock() time.Sleep(timeToExpire) c.mu.Lock() } for _, ent := range c.buckets[bucketIdx].entries { c.removeElement(ent) } c.nextCleanupBucket = (c.nextCleanupBucket + 1) % numBuckets c.mu.Unlock() } BucketΛۭʹ͢Δ Bucket಺ͷEntryͷੜଘظ͕ؒ੾ΕΔ·Ͱ଴ͭ
  30. Expirable LRUͷ಺෦࣮૷ΛಡΉ 39 GetΛݟͯΈΔ func (c *LRU[K, V]) Get(key K)

    (value V, ok bool) { c.mu.Lock() defer c.mu.Unlock() var ent *internal.Entry[K, V] if ent, ok = c.items[key]; ok { // Expired item check if time.Now().After(ent.ExpiresAt) { return value, false } c.evictList.MoveToFront(ent) return ent.Value, true } return } Mapʹଘࡏͯ͠΋ TTL௒͑ͨ΋ͷ͸ฦ͞ͳ͍