Skip to content

Commit

Permalink
提取 Index 方法
Browse files Browse the repository at this point in the history
  • Loading branch information
FishGoddess committed Oct 4, 2022
1 parent 46dce6d commit 04329a4
Show file tree
Hide file tree
Showing 22 changed files with 167 additions and 52 deletions.
16 changes: 16 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Test Project

on:
push:
branches:
workflow_dispatch:

jobs:
test-project:
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Test
run: make test
7 changes: 7 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
## ✒ 历史版本的特性介绍 (Features in old versions)

### v0.3.7

> 此版本发布于 2022-10-04
* 提取 Index 函数,用户可以自定义哈希算法
* 大量优化代码风格

### v0.3.6

> 此版本发布于 2022-05-09
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [2020] [Ye Zi Jie]
Copyright [2020] [FishGoddess]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
.PHONY: test bench benchfile fmt

all: test bench

test:
go test -v -cover ./...
go test -cover ./...

bench:
go test -v ./_examples/performance_test.go
go test -v ./_examples/performance_test.go -benchtime=1s

fmt:
go fmt ./...
6 changes: 2 additions & 4 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

[![Go Doc](_icons/godoc.svg)](https://pkg.go.dev/github.com/FishGoddess/cachego)
[![License](_icons/license.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
[![License](_icons/build.svg)](_icons/build.svg)
[![License](_icons/coverage.svg)](_icons/coverage.svg)
![Test](https://github.com/FishGoddess/cachego/actions/workflows/test.yml/badge.svg)

**cachego** is a high-performance and memory-based cache for [GoLang](https://golang.org) applications.

> It has been used by many services in production, and even 17w/s qps is ok for it, so just use it if you want!
> I am developing v0.3.x which will be better at APIs and features, so issue me if you have something fun!!!
[阅读中文版的 Read me](./README.md).

### 🕹 Features
Expand All @@ -23,7 +21,7 @@
* Singleflight supports, which can decrease the times of cache penetration
* ....

_More features in [_examples](_examples) and designing detail in [arch.md](_examples/docs/arch.md)._
_More features in [_examples](_examples) and designing detail in [introduction.md](_examples/docs/introduction.md)._

_Check [HISTORY.md](./HISTORY.md) and [FUTURE.md](./FUTURE.md) to get more information._

Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

[![Go Doc](_icons/godoc.svg)](https://pkg.go.dev/github.com/FishGoddess/cachego)
[![License](_icons/license.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
[![License](_icons/build.svg)](_icons/build.svg)
[![License](_icons/coverage.svg)](_icons/coverage.svg)
![Test](https://github.com/FishGoddess/cachego/actions/workflows/test.yml/badge.svg)

**cachego** 是一个拥有高性能分段锁机制的轻量级内存缓存,拥有懒清理和哨兵清理两种清理机制,可以应用于所有的 [GoLang](https://golang.org) 应用程序中。

> 目前已经在多个线上服务中运行良好,也抵御过最高 17w/s qps 的冲击,可以稳定使用!
> 我正在开发 v0.3.x 版本,这将在 API 以及功能上达到全新的使用体验,敬请期待,也期待大家的建议!!!
[Read me in English](./README.en.md).

### 🕹 功能特性
Expand All @@ -23,7 +21,7 @@
* 自带 singleflight 机制,减少缓存穿透的伤害
* ....

_更多功能请参考 [_examples](_examples)架构设计请参考 [arch.md](_examples/docs/arch.md) 文档。_
_更多功能请参考 [_examples](_examples)设计信息请参考 [introduction.md](_examples/docs/introduction.md) 文档。_

_历史版本的特性请查看 [HISTORY.md](./HISTORY.md)。未来版本的新特性和计划请查看 [FUTURE.md](./FUTURE.md)_

Expand Down
2 changes: 1 addition & 1 deletion _examples/docs/arch.md → _examples/docs/introduction.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## ✒ Cachego 架构介绍
## ✒ Cachego 介绍

cachego 是一个拥有高性能分段锁机制的轻量级内存缓存,拥有懒清理和哨兵清理两种清理机制,可以应用于所有的 GoLang 应用程序中。

Expand Down
14 changes: 14 additions & 0 deletions _examples/reload.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
// Copyright 2022 FishGoddess. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
Expand Down
6 changes: 6 additions & 0 deletions _examples/singleflight.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)

go func() {
defer wg.Done()

Expand All @@ -41,12 +42,14 @@ func main() {
}))
}()
}

wg.Wait()

// If you want to disable single-flight mode in some Get operations, try this:
wg = sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)

go func() {
defer wg.Done()

Expand All @@ -57,6 +60,7 @@ func main() {
}), cachego.WithOpDisableSingleflight())
}()
}

wg.Wait()

// Of course, we all know single-flight mode will decrease the success rate of loading data.
Expand All @@ -66,6 +70,7 @@ func main() {
wg = sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)

go func() {
defer wg.Done()

Expand All @@ -76,5 +81,6 @@ func main() {
}))
}()
}

wg.Wait()
}
16 changes: 0 additions & 16 deletions _icons/build.svg

This file was deleted.

4 changes: 2 additions & 2 deletions _icons/coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 9 additions & 16 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,15 @@ func NewCache(opts ...Option) *Cache {
}

c.segments = newSegments(c.conf.mapSize, c.conf.segmentSize)

if c.conf.singleflight {
c.groups = newGroups(c.conf.mapSize, c.conf.segmentSize)
}

if c.conf.gcDuration > 0 {
c.AutoGC(c.conf.gcDuration)
}

return c
}

Expand All @@ -66,6 +68,7 @@ func newSegments(mapSize int, segmentSize int) []*segment {
for i := 0; i < segmentSize; i++ {
segments[i] = newSegment(mapSize)
}

return segments
}

Expand All @@ -75,38 +78,26 @@ func newGroups(mapSize int, groupSize int) []*singleflight.Group {
for i := 0; i < groupSize; i++ {
groups[i] = singleflight.NewGroup(mapSize)
}
return groups
}

// indexOf returns a position of this key.
func (c *Cache) indexOf(key string) int {
index := 1469598103934665603
keyBytes := []byte(key)

for _, b := range keyBytes {
index = (index << 5) - index + int(b&0xff)
index *= 1099511628211
}
return index
return groups
}

// segmentOf returns the segment of this key.
func (c *Cache) segmentOf(key string) *segment {
return c.segments[c.indexOf(key)&(len(c.segments)-1)]
return c.segments[Index(key)&(len(c.segments)-1)]
}

// groupOf returns the singleflight group of this key.
func (c *Cache) groupOf(key string) *singleflight.Group {
return c.groups[c.indexOf(key)&(len(c.groups)-1)]
return c.groups[Index(key)&(len(c.groups)-1)]
}

// Get fetches value of key from cache first, and returns it if ok.
// Returns an NotFoundErr if this key is not found, and you can use IsNotFound to judge if this error is not found.
// Also, you can specify a function which will be called if missed, so you can load this entry to cache again.
// See OpOption.
func (c *Cache) Get(key string, opts ...OpOption) (interface{}, error) {
v, ok := c.segmentOf(key).get(key)
if ok {
if v, ok := c.segmentOf(key).get(key); ok {
return v, nil
}

Expand Down Expand Up @@ -134,6 +125,7 @@ func (c *Cache) Get(key string, opts ...OpOption) (interface{}, error) {
if conf.reload {
c.Set(key, data, WithOpTTL(conf.ttl))
}

return data, nil
}

Expand Down Expand Up @@ -170,6 +162,7 @@ func (c *Cache) Size() int {
for _, segment := range c.segments {
size += segment.size()
}

return size
}

Expand Down
11 changes: 11 additions & 0 deletions cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,25 @@ func TestCache(t *testing.T) {
key := "key"
value := 123
cache.Set(key, value)

if v, err := cache.Get(key); IsNotFound(err) || v != value {
t.Error("Before reset cache, cache.Of(key) returns wrong err or value!")
}

cache.DeleteAll()

if _, err := cache.Get(key); !IsNotFound(err) || cache.Size() != 0 {
t.Error("Cache should be reset!")
}

cache.Set(key, value)

if v, err := cache.Get(key); IsNotFound(err) || v != value {
t.Error("Before delete key, cache.Of(key) returns wrong err or value!")
}

cache.Delete(key)

if _, err := cache.Get(key); !IsNotFound(err) {
t.Error("After deleting key, key should be dead!")
}
Expand All @@ -55,11 +59,13 @@ func TestCacheTTL(t *testing.T) {
key := "key"
value := 123
cache.Set(key, value, WithOpTTL(1*time.Second))

if v, err := cache.Get(key); IsNotFound(err) || cache.Size() != 1 || v != value {
t.Error("Before ttl, returns wrong err or size or value!")
}

time.Sleep(2 * time.Second)

if _, err := cache.Get(key); !IsNotFound(err) {
t.Error("After ttl, key should be dead!")
}
Expand All @@ -69,6 +75,7 @@ func TestCacheTTL(t *testing.T) {
}

time.Sleep(2 * time.Second)

if cache.Size() != 0 {
t.Error("After gc, size should be 0!")
}
Expand All @@ -85,11 +92,13 @@ func TestGetWithLoad(t *testing.T) {
key := "key"
value := "get"
cache.Set(key, value, WithOpTTL(1*time.Second))

if v, err := cache.Get(key, WithOpOnMissed(loadFunc), WithOpTTL(time.Second)); err != nil || v.(string) != value {
t.Errorf("Before Sleep, cache.Of(key) returns err %+v or wrong value %s!", err, v.(string))
}

time.Sleep(2 * time.Second)

if v, err := cache.Get(key, WithOpOnMissed(loadFunc), WithOpTTL(time.Second)); err != nil || v.(string) != "loadFunc" {
t.Errorf("After Sleep, cache.Of(key) returns err %+v or wrong value %s!", err, v.(string))
}
Expand All @@ -103,11 +112,13 @@ func TestCacheAutoGC(t *testing.T) {
key := "key"
value := 123
cache.Set(key, value, WithOpTTL(1*time.Second))

if v, err := cache.Get(key); IsNotFound(err) || cache.Size() != 1 || v != value {
t.Error("Before gc, returns wrong err or size or value!")
}

time.Sleep(3 * time.Second)

if _, err := cache.Get(key); !IsNotFound(err) || cache.Size() != 1 {
t.Error("After gc, key should be dead!")
}
Expand Down
Loading

0 comments on commit 04329a4

Please sign in to comment.