Skip to content

Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

go-cache

Go DocBuild StatusGo Report CardcodecovCoverage StatusMaintainabilitySourcegraphLicenseFOSSA StatusMentioned in Awesome Go

Photo by Ashley McNamara, via ashleymcnamara/gophers (CC BY-NC-SA 4.0)

A flexible multi-layered caching library interacts with private (in-memory) cache and shared cache (i.e. Redis) in Go. It provides Cache-Aside strategy when dealing with both, and maintains the consistency of private cache between distributed systems by Pub-Sub pattern.

Caching is a common technique that aims to improve the performance and scalability of a system. It does this by temporarily copying frequently accessed data to fast storage close to the application. Distributed applications typically implement either or both of the following strategies when caching data:

  • Using a private cache, where data is held locally on the computer that's running an instance of an application or service.
  • Using a shared cache, serving as a common source that can be accessed by multiple processes and machines.

Using a local private cache with a shared cache Ref: https://docs.microsoft.com/en-us/azure/architecture/best-practices/images/caching/caching3.png

Considering the flexibility, efficiency and consistency, we starts to build up our own framework.

Features

  • Easy to use : provide a friendly interface to deal with both caching mechnaism by simple configuration. Limit the size of memory on single instance (pod) as well.
  • Maintain consistency : evict keys between distributed systems by Pub-Sub pattern.
  • Data compression : provide customized marshal and unmarshal functions.
  • Fix concurrency issue : prevent data racing happened on single instance (pod).
  • Metric : provide callback functions to measure the performance. (i.e. hit rate, private cache usage, ...)

Data flow

Load the cache with Cache-Aside strategy

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant R as Resource (Microservice / DB)
APP ->> M: Cache.Get() / Cache.MGet()
alt Local Cache hit
M ->> L: Adapter.MGet()
L -->> M: {[]Value, error}
M -->> APP: return
else Local Cache miss but Shared Cache hit
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: {[]Value, error}
M ->> L: Adapter.MSet()
M -->> APP: return
else All miss
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: cache miss
M ->> R: OneTimeGetterFunc() / MGetterFunc()
R -->> M: return from getter
M ->> S: Adapter.MSet()
M ->> L: Adapter.MSet()
M -->> APP: return
end
Loading

Evict the cache

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant PS as PubSub
APP ->> M: Cache.Del()
M ->> S: Adapter.Del()
S -->> M: return error if necessary
M ->> L: Adapter.Del()
L -->> M: return error if necessary
M ->> PS: Pubsub.Pub() (broadcast key eviction)
M -->> APP: return nil or error
Loading

Installation

go get github.com/viney-shih/go-cache

Get Started

Basic usage: Set-And-Get

By adopting Singleton pattern, initialize the Factory in main.go at the beginning, and deliver it to each package or business logic.

// Initialize the Factory in main.gotinyLfu:=cache.NewTinyLFU(10000)
rds:=cache.NewRedis(redis.NewRing(&redis.RingOptions{
Addrs: map[string]string{
"server1": ":6379",
},
}))
cacheFactory:=cache.NewFactory(rds, tinyLfu)

Treat it as a common key:value store like Redis. But more advanced, it coordinated the usage between multi-layered caching mechanism inside.

typeObjectstruct {
StrstringNumint
}
funcExample_setAndGetPattern() {
// We create a group of cache named "set-and-get".// It uses the shared cache only. Each key will be expired within ten seconds.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "set-and-get",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: 10*time.Second},
},
},
})
ctx:=context.TODO()
// set the cacheobj:=&Object{
Str: "value1",
Num: 1,
}
iferr:=c.Set(ctx, "set-and-get", "key", obj); err!=nil {
panic("not expected")
}
// read the cachecontainer:=&Object{}
iferr:=c.Get(ctx, "set-and-get", "key", container); err!=nil {
panic("not expected")
}
fmt.Println(container) // Output: Object{ Str: "value1", Num: 1}// read the cache but failediferr:=c.Get(ctx, "set-and-get", "no-such-key", container); err!=nil {
fmt.Println(err) // Output: errors.New("cache key is missing")
}
// Output:// &{value1 1}// cache key is missing
}

Advanced usage: Cache-Aside strategy

GetByFunc() is the easier way to deal with the cache by implementing the getter function in the parameter. When the cache is missing, it is going to refill the cache automatically.

funcExampleCache_GetByFunc() {
// We create a group of cache named "get-by-func".// It uses the local cache only with TTL of ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "get-by-func",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MarshalFunc: msgpack.Marshal, // msgpack is from "github.com/vmihailenco/msgpack/v5"UnmarshalFunc: msgpack.Unmarshal,
},
})
ctx:=context.TODO()
container2:=&Object{}
iferr:=c.GetByFunc(ctx, "get-by-func", "key2", container2, func() (interface{}, error) {
// The getter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key2" and get the value of Object{Str: "value2", Num: 2}returnObject{Str: "value2", Num: 2}, nil
}); err!=nil {
panic("not expected")
}
fmt.Println(container2) // Object{ Str: "value2", Num: 2}// Output:// &{value2 2}
}

MGetter is another approaching way to do this. Set this function durning registering the Setting.

funcExampleService_Create_mGetter() {
// We create a group of cache named "mgetter".// It uses both shared and local caches with separated TTL of one hour and ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "mgetter",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: time.Hour},
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MGetter: func(keys...string) (interface{}, error) {
// The MGetter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key3" and get the value of Object{Str: "value3", Num: 3}// HINT: remember to return as a slice, and the item order needs to consist with the keys in the parameters.return []Object{{Str: "value3", Num: 3}}, nil
},
MarshalFunc: cache.Marshal,
UnmarshalFunc: cache.Unmarshal,
},
})
ctx:=context.TODO()
container3:=&Object{}
iferr:=c.Get(ctx, "mgetter", "key3", container3); err!=nil {
panic("not expected")
}
fmt.Println(container3) // Object{ Str: "value3", Num: 3}// Output:// &{value3 3}
}

More examples

References

License

Apache-2.0

FOSSA Status

About

A flexible multi-layer Go caching library to deal with in-memory and shared cache by adopting Cache-Aside pattern.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - nicknameDZ/go-cache: A flexible multi-layer Go caching library to deal with in-memory and shared cache by adopting Cache-Aside pattern. · GitHub
Skip to content

Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

go-cache

Go DocBuild StatusGo Report CardcodecovCoverage StatusMaintainabilitySourcegraphLicenseFOSSA StatusMentioned in Awesome Go

Photo by Ashley McNamara, via ashleymcnamara/gophers (CC BY-NC-SA 4.0)

A flexible multi-layered caching library interacts with private (in-memory) cache and shared cache (i.e. Redis) in Go. It provides Cache-Aside strategy when dealing with both, and maintains the consistency of private cache between distributed systems by Pub-Sub pattern.

Caching is a common technique that aims to improve the performance and scalability of a system. It does this by temporarily copying frequently accessed data to fast storage close to the application. Distributed applications typically implement either or both of the following strategies when caching data:

  • Using a private cache, where data is held locally on the computer that's running an instance of an application or service.
  • Using a shared cache, serving as a common source that can be accessed by multiple processes and machines.

Using a local private cache with a shared cache Ref: https://docs.microsoft.com/en-us/azure/architecture/best-practices/images/caching/caching3.png

Considering the flexibility, efficiency and consistency, we starts to build up our own framework.

Features

  • Easy to use : provide a friendly interface to deal with both caching mechnaism by simple configuration. Limit the size of memory on single instance (pod) as well.
  • Maintain consistency : evict keys between distributed systems by Pub-Sub pattern.
  • Data compression : provide customized marshal and unmarshal functions.
  • Fix concurrency issue : prevent data racing happened on single instance (pod).
  • Metric : provide callback functions to measure the performance. (i.e. hit rate, private cache usage, ...)

Data flow

Load the cache with Cache-Aside strategy

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant R as Resource (Microservice / DB)
APP ->> M: Cache.Get() / Cache.MGet()
alt Local Cache hit
M ->> L: Adapter.MGet()
L -->> M: {[]Value, error}
M -->> APP: return
else Local Cache miss but Shared Cache hit
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: {[]Value, error}
M ->> L: Adapter.MSet()
M -->> APP: return
else All miss
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: cache miss
M ->> R: OneTimeGetterFunc() / MGetterFunc()
R -->> M: return from getter
M ->> S: Adapter.MSet()
M ->> L: Adapter.MSet()
M -->> APP: return
end
Loading

Evict the cache

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant PS as PubSub
APP ->> M: Cache.Del()
M ->> S: Adapter.Del()
S -->> M: return error if necessary
M ->> L: Adapter.Del()
L -->> M: return error if necessary
M ->> PS: Pubsub.Pub() (broadcast key eviction)
M -->> APP: return nil or error
Loading

Installation

go get github.com/viney-shih/go-cache

Get Started

Basic usage: Set-And-Get

By adopting Singleton pattern, initialize the Factory in main.go at the beginning, and deliver it to each package or business logic.

// Initialize the Factory in main.gotinyLfu:=cache.NewTinyLFU(10000)
rds:=cache.NewRedis(redis.NewRing(&redis.RingOptions{
Addrs: map[string]string{
"server1": ":6379",
},
}))
cacheFactory:=cache.NewFactory(rds, tinyLfu)

Treat it as a common key:value store like Redis. But more advanced, it coordinated the usage between multi-layered caching mechanism inside.

typeObjectstruct {
StrstringNumint
}
funcExample_setAndGetPattern() {
// We create a group of cache named "set-and-get".// It uses the shared cache only. Each key will be expired within ten seconds.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "set-and-get",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: 10*time.Second},
},
},
})
ctx:=context.TODO()
// set the cacheobj:=&Object{
Str: "value1",
Num: 1,
}
iferr:=c.Set(ctx, "set-and-get", "key", obj); err!=nil {
panic("not expected")
}
// read the cachecontainer:=&Object{}
iferr:=c.Get(ctx, "set-and-get", "key", container); err!=nil {
panic("not expected")
}
fmt.Println(container) // Output: Object{ Str: "value1", Num: 1}// read the cache but failediferr:=c.Get(ctx, "set-and-get", "no-such-key", container); err!=nil {
fmt.Println(err) // Output: errors.New("cache key is missing")
}
// Output:// &{value1 1}// cache key is missing
}

Advanced usage: Cache-Aside strategy

GetByFunc() is the easier way to deal with the cache by implementing the getter function in the parameter. When the cache is missing, it is going to refill the cache automatically.

funcExampleCache_GetByFunc() {
// We create a group of cache named "get-by-func".// It uses the local cache only with TTL of ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "get-by-func",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MarshalFunc: msgpack.Marshal, // msgpack is from "github.com/vmihailenco/msgpack/v5"UnmarshalFunc: msgpack.Unmarshal,
},
})
ctx:=context.TODO()
container2:=&Object{}
iferr:=c.GetByFunc(ctx, "get-by-func", "key2", container2, func() (interface{}, error) {
// The getter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key2" and get the value of Object{Str: "value2", Num: 2}returnObject{Str: "value2", Num: 2}, nil
}); err!=nil {
panic("not expected")
}
fmt.Println(container2) // Object{ Str: "value2", Num: 2}// Output:// &{value2 2}
}

MGetter is another approaching way to do this. Set this function durning registering the Setting.

funcExampleService_Create_mGetter() {
// We create a group of cache named "mgetter".// It uses both shared and local caches with separated TTL of one hour and ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "mgetter",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: time.Hour},
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MGetter: func(keys...string) (interface{}, error) {
// The MGetter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key3" and get the value of Object{Str: "value3", Num: 3}// HINT: remember to return as a slice, and the item order needs to consist with the keys in the parameters.return []Object{{Str: "value3", Num: 3}}, nil
},
MarshalFunc: cache.Marshal,
UnmarshalFunc: cache.Unmarshal,
},
})
ctx:=context.TODO()
container3:=&Object{}
iferr:=c.Get(ctx, "mgetter", "key3", container3); err!=nil {
panic("not expected")
}
fmt.Println(container3) // Object{ Str: "value3", Num: 3}// Output:// &{value3 3}
}

More examples

References

License

Apache-2.0

FOSSA Status

About

A flexible multi-layer Go caching library to deal with in-memory and shared cache by adopting Cache-Aside pattern.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - nicknameDZ/go-cache: A flexible multi-layer Go caching library to deal with in-memory and shared cache by adopting Cache-Aside pattern. · GitHub
Skip to content

Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

go-cache

Go DocBuild StatusGo Report CardcodecovCoverage StatusMaintainabilitySourcegraphLicenseFOSSA StatusMentioned in Awesome Go

Photo by Ashley McNamara, via ashleymcnamara/gophers (CC BY-NC-SA 4.0)

A flexible multi-layered caching library interacts with private (in-memory) cache and shared cache (i.e. Redis) in Go. It provides Cache-Aside strategy when dealing with both, and maintains the consistency of private cache between distributed systems by Pub-Sub pattern.

Caching is a common technique that aims to improve the performance and scalability of a system. It does this by temporarily copying frequently accessed data to fast storage close to the application. Distributed applications typically implement either or both of the following strategies when caching data:

  • Using a private cache, where data is held locally on the computer that's running an instance of an application or service.
  • Using a shared cache, serving as a common source that can be accessed by multiple processes and machines.

Using a local private cache with a shared cache Ref: https://docs.microsoft.com/en-us/azure/architecture/best-practices/images/caching/caching3.png

Considering the flexibility, efficiency and consistency, we starts to build up our own framework.

Features

  • Easy to use : provide a friendly interface to deal with both caching mechnaism by simple configuration. Limit the size of memory on single instance (pod) as well.
  • Maintain consistency : evict keys between distributed systems by Pub-Sub pattern.
  • Data compression : provide customized marshal and unmarshal functions.
  • Fix concurrency issue : prevent data racing happened on single instance (pod).
  • Metric : provide callback functions to measure the performance. (i.e. hit rate, private cache usage, ...)

Data flow

Load the cache with Cache-Aside strategy

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant R as Resource (Microservice / DB)
APP ->> M: Cache.Get() / Cache.MGet()
alt Local Cache hit
M ->> L: Adapter.MGet()
L -->> M: {[]Value, error}
M -->> APP: return
else Local Cache miss but Shared Cache hit
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: {[]Value, error}
M ->> L: Adapter.MSet()
M -->> APP: return
else All miss
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: cache miss
M ->> R: OneTimeGetterFunc() / MGetterFunc()
R -->> M: return from getter
M ->> S: Adapter.MSet()
M ->> L: Adapter.MSet()
M -->> APP: return
end
Loading

Evict the cache

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant PS as PubSub
APP ->> M: Cache.Del()
M ->> S: Adapter.Del()
S -->> M: return error if necessary
M ->> L: Adapter.Del()
L -->> M: return error if necessary
M ->> PS: Pubsub.Pub() (broadcast key eviction)
M -->> APP: return nil or error
Loading

Installation

go get github.com/viney-shih/go-cache

Get Started

Basic usage: Set-And-Get

By adopting Singleton pattern, initialize the Factory in main.go at the beginning, and deliver it to each package or business logic.

// Initialize the Factory in main.gotinyLfu:=cache.NewTinyLFU(10000)
rds:=cache.NewRedis(redis.NewRing(&redis.RingOptions{
Addrs: map[string]string{
"server1": ":6379",
},
}))
cacheFactory:=cache.NewFactory(rds, tinyLfu)

Treat it as a common key:value store like Redis. But more advanced, it coordinated the usage between multi-layered caching mechanism inside.

typeObjectstruct {
StrstringNumint
}
funcExample_setAndGetPattern() {
// We create a group of cache named "set-and-get".// It uses the shared cache only. Each key will be expired within ten seconds.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "set-and-get",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: 10*time.Second},
},
},
})
ctx:=context.TODO()
// set the cacheobj:=&Object{
Str: "value1",
Num: 1,
}
iferr:=c.Set(ctx, "set-and-get", "key", obj); err!=nil {
panic("not expected")
}
// read the cachecontainer:=&Object{}
iferr:=c.Get(ctx, "set-and-get", "key", container); err!=nil {
panic("not expected")
}
fmt.Println(container) // Output: Object{ Str: "value1", Num: 1}// read the cache but failediferr:=c.Get(ctx, "set-and-get", "no-such-key", container); err!=nil {
fmt.Println(err) // Output: errors.New("cache key is missing")
}
// Output:// &{value1 1}// cache key is missing
}

Advanced usage: Cache-Aside strategy

GetByFunc() is the easier way to deal with the cache by implementing the getter function in the parameter. When the cache is missing, it is going to refill the cache automatically.

funcExampleCache_GetByFunc() {
// We create a group of cache named "get-by-func".// It uses the local cache only with TTL of ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "get-by-func",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MarshalFunc: msgpack.Marshal, // msgpack is from "github.com/vmihailenco/msgpack/v5"UnmarshalFunc: msgpack.Unmarshal,
},
})
ctx:=context.TODO()
container2:=&Object{}
iferr:=c.GetByFunc(ctx, "get-by-func", "key2", container2, func() (interface{}, error) {
// The getter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key2" and get the value of Object{Str: "value2", Num: 2}returnObject{Str: "value2", Num: 2}, nil
}); err!=nil {
panic("not expected")
}
fmt.Println(container2) // Object{ Str: "value2", Num: 2}// Output:// &{value2 2}
}

MGetter is another approaching way to do this. Set this function durning registering the Setting.

funcExampleService_Create_mGetter() {
// We create a group of cache named "mgetter".// It uses both shared and local caches with separated TTL of one hour and ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "mgetter",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: time.Hour},
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MGetter: func(keys...string) (interface{}, error) {
// The MGetter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key3" and get the value of Object{Str: "value3", Num: 3}// HINT: remember to return as a slice, and the item order needs to consist with the keys in the parameters.return []Object{{Str: "value3", Num: 3}}, nil
},
MarshalFunc: cache.Marshal,
UnmarshalFunc: cache.Unmarshal,
},
})
ctx:=context.TODO()
container3:=&Object{}
iferr:=c.Get(ctx, "mgetter", "key3", container3); err!=nil {
panic("not expected")
}
fmt.Println(container3) // Object{ Str: "value3", Num: 3}// Output:// &{value3 3}
}

More examples

References

License

Apache-2.0

FOSSA Status

About

A flexible multi-layer Go caching library to deal with in-memory and shared cache by adopting Cache-Aside pattern.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - nicknameDZ/go-cache: A flexible multi-layer Go caching library to deal with in-memory and shared cache by adopting Cache-Aside pattern. · GitHub
Skip to content

Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

go-cache

Go DocBuild StatusGo Report CardcodecovCoverage StatusMaintainabilitySourcegraphLicenseFOSSA StatusMentioned in Awesome Go

Photo by Ashley McNamara, via ashleymcnamara/gophers (CC BY-NC-SA 4.0)

A flexible multi-layered caching library interacts with private (in-memory) cache and shared cache (i.e. Redis) in Go. It provides Cache-Aside strategy when dealing with both, and maintains the consistency of private cache between distributed systems by Pub-Sub pattern.

Caching is a common technique that aims to improve the performance and scalability of a system. It does this by temporarily copying frequently accessed data to fast storage close to the application. Distributed applications typically implement either or both of the following strategies when caching data:

  • Using a private cache, where data is held locally on the computer that's running an instance of an application or service.
  • Using a shared cache, serving as a common source that can be accessed by multiple processes and machines.

Using a local private cache with a shared cache Ref: https://docs.microsoft.com/en-us/azure/architecture/best-practices/images/caching/caching3.png

Considering the flexibility, efficiency and consistency, we starts to build up our own framework.

Features

  • Easy to use : provide a friendly interface to deal with both caching mechnaism by simple configuration. Limit the size of memory on single instance (pod) as well.
  • Maintain consistency : evict keys between distributed systems by Pub-Sub pattern.
  • Data compression : provide customized marshal and unmarshal functions.
  • Fix concurrency issue : prevent data racing happened on single instance (pod).
  • Metric : provide callback functions to measure the performance. (i.e. hit rate, private cache usage, ...)

Data flow

Load the cache with Cache-Aside strategy

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant R as Resource (Microservice / DB)
APP ->> M: Cache.Get() / Cache.MGet()
alt Local Cache hit
M ->> L: Adapter.MGet()
L -->> M: {[]Value, error}
M -->> APP: return
else Local Cache miss but Shared Cache hit
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: {[]Value, error}
M ->> L: Adapter.MSet()
M -->> APP: return
else All miss
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: cache miss
M ->> R: OneTimeGetterFunc() / MGetterFunc()
R -->> M: return from getter
M ->> S: Adapter.MSet()
M ->> L: Adapter.MSet()
M -->> APP: return
end
Loading

Evict the cache

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant PS as PubSub
APP ->> M: Cache.Del()
M ->> S: Adapter.Del()
S -->> M: return error if necessary
M ->> L: Adapter.Del()
L -->> M: return error if necessary
M ->> PS: Pubsub.Pub() (broadcast key eviction)
M -->> APP: return nil or error
Loading

Installation

go get github.com/viney-shih/go-cache

Get Started

Basic usage: Set-And-Get

By adopting Singleton pattern, initialize the Factory in main.go at the beginning, and deliver it to each package or business logic.

// Initialize the Factory in main.gotinyLfu:=cache.NewTinyLFU(10000)
rds:=cache.NewRedis(redis.NewRing(&redis.RingOptions{
Addrs: map[string]string{
"server1": ":6379",
},
}))
cacheFactory:=cache.NewFactory(rds, tinyLfu)

Treat it as a common key:value store like Redis. But more advanced, it coordinated the usage between multi-layered caching mechanism inside.

typeObjectstruct {
StrstringNumint
}
funcExample_setAndGetPattern() {
// We create a group of cache named "set-and-get".// It uses the shared cache only. Each key will be expired within ten seconds.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "set-and-get",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: 10*time.Second},
},
},
})
ctx:=context.TODO()
// set the cacheobj:=&Object{
Str: "value1",
Num: 1,
}
iferr:=c.Set(ctx, "set-and-get", "key", obj); err!=nil {
panic("not expected")
}
// read the cachecontainer:=&Object{}
iferr:=c.Get(ctx, "set-and-get", "key", container); err!=nil {
panic("not expected")
}
fmt.Println(container) // Output: Object{ Str: "value1", Num: 1}// read the cache but failediferr:=c.Get(ctx, "set-and-get", "no-such-key", container); err!=nil {
fmt.Println(err) // Output: errors.New("cache key is missing")
}
// Output:// &{value1 1}// cache key is missing
}

Advanced usage: Cache-Aside strategy

GetByFunc() is the easier way to deal with the cache by implementing the getter function in the parameter. When the cache is missing, it is going to refill the cache automatically.

funcExampleCache_GetByFunc() {
// We create a group of cache named "get-by-func".// It uses the local cache only with TTL of ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "get-by-func",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MarshalFunc: msgpack.Marshal, // msgpack is from "github.com/vmihailenco/msgpack/v5"UnmarshalFunc: msgpack.Unmarshal,
},
})
ctx:=context.TODO()
container2:=&Object{}
iferr:=c.GetByFunc(ctx, "get-by-func", "key2", container2, func() (interface{}, error) {
// The getter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key2" and get the value of Object{Str: "value2", Num: 2}returnObject{Str: "value2", Num: 2}, nil
}); err!=nil {
panic("not expected")
}
fmt.Println(container2) // Object{ Str: "value2", Num: 2}// Output:// &{value2 2}
}

MGetter is another approaching way to do this. Set this function durning registering the Setting.

funcExampleService_Create_mGetter() {
// We create a group of cache named "mgetter".// It uses both shared and local caches with separated TTL of one hour and ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "mgetter",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: time.Hour},
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MGetter: func(keys...string) (interface{}, error) {
// The MGetter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key3" and get the value of Object{Str: "value3", Num: 3}// HINT: remember to return as a slice, and the item order needs to consist with the keys in the parameters.return []Object{{Str: "value3", Num: 3}}, nil
},
MarshalFunc: cache.Marshal,
UnmarshalFunc: cache.Unmarshal,
},
})
ctx:=context.TODO()
container3:=&Object{}
iferr:=c.Get(ctx, "mgetter", "key3", container3); err!=nil {
panic("not expected")
}
fmt.Println(container3) // Object{ Str: "value3", Num: 3}// Output:// &{value3 3}
}

More examples

References

License

Apache-2.0

FOSSA Status

About

A flexible multi-layer Go caching library to deal with in-memory and shared cache by adopting Cache-Aside pattern.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - nicknameDZ/go-cache: A flexible multi-layer Go caching library to deal with in-memory and shared cache by adopting Cache-Aside pattern. · GitHub
Skip to content

Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

go-cache

Go DocBuild StatusGo Report CardcodecovCoverage StatusMaintainabilitySourcegraphLicenseFOSSA StatusMentioned in Awesome Go

Photo by Ashley McNamara, via ashleymcnamara/gophers (CC BY-NC-SA 4.0)

A flexible multi-layered caching library interacts with private (in-memory) cache and shared cache (i.e. Redis) in Go. It provides Cache-Aside strategy when dealing with both, and maintains the consistency of private cache between distributed systems by Pub-Sub pattern.

Caching is a common technique that aims to improve the performance and scalability of a system. It does this by temporarily copying frequently accessed data to fast storage close to the application. Distributed applications typically implement either or both of the following strategies when caching data:

  • Using a private cache, where data is held locally on the computer that's running an instance of an application or service.
  • Using a shared cache, serving as a common source that can be accessed by multiple processes and machines.

Using a local private cache with a shared cache Ref: https://docs.microsoft.com/en-us/azure/architecture/best-practices/images/caching/caching3.png

Considering the flexibility, efficiency and consistency, we starts to build up our own framework.

Features

  • Easy to use : provide a friendly interface to deal with both caching mechnaism by simple configuration. Limit the size of memory on single instance (pod) as well.
  • Maintain consistency : evict keys between distributed systems by Pub-Sub pattern.
  • Data compression : provide customized marshal and unmarshal functions.
  • Fix concurrency issue : prevent data racing happened on single instance (pod).
  • Metric : provide callback functions to measure the performance. (i.e. hit rate, private cache usage, ...)

Data flow

Load the cache with Cache-Aside strategy

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant R as Resource (Microservice / DB)
APP ->> M: Cache.Get() / Cache.MGet()
alt Local Cache hit
M ->> L: Adapter.MGet()
L -->> M: {[]Value, error}
M -->> APP: return
else Local Cache miss but Shared Cache hit
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: {[]Value, error}
M ->> L: Adapter.MSet()
M -->> APP: return
else All miss
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: cache miss
M ->> R: OneTimeGetterFunc() / MGetterFunc()
R -->> M: return from getter
M ->> S: Adapter.MSet()
M ->> L: Adapter.MSet()
M -->> APP: return
end
Loading

Evict the cache

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant PS as PubSub
APP ->> M: Cache.Del()
M ->> S: Adapter.Del()
S -->> M: return error if necessary
M ->> L: Adapter.Del()
L -->> M: return error if necessary
M ->> PS: Pubsub.Pub() (broadcast key eviction)
M -->> APP: return nil or error
Loading

Installation

go get github.com/viney-shih/go-cache

Get Started

Basic usage: Set-And-Get

By adopting Singleton pattern, initialize the Factory in main.go at the beginning, and deliver it to each package or business logic.

// Initialize the Factory in main.gotinyLfu:=cache.NewTinyLFU(10000)
rds:=cache.NewRedis(redis.NewRing(&redis.RingOptions{
Addrs: map[string]string{
"server1": ":6379",
},
}))
cacheFactory:=cache.NewFactory(rds, tinyLfu)

Treat it as a common key:value store like Redis. But more advanced, it coordinated the usage between multi-layered caching mechanism inside.

typeObjectstruct {
StrstringNumint
}
funcExample_setAndGetPattern() {
// We create a group of cache named "set-and-get".// It uses the shared cache only. Each key will be expired within ten seconds.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "set-and-get",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: 10*time.Second},
},
},
})
ctx:=context.TODO()
// set the cacheobj:=&Object{
Str: "value1",
Num: 1,
}
iferr:=c.Set(ctx, "set-and-get", "key", obj); err!=nil {
panic("not expected")
}
// read the cachecontainer:=&Object{}
iferr:=c.Get(ctx, "set-and-get", "key", container); err!=nil {
panic("not expected")
}
fmt.Println(container) // Output: Object{ Str: "value1", Num: 1}// read the cache but failediferr:=c.Get(ctx, "set-and-get", "no-such-key", container); err!=nil {
fmt.Println(err) // Output: errors.New("cache key is missing")
}
// Output:// &{value1 1}// cache key is missing
}

Advanced usage: Cache-Aside strategy

GetByFunc() is the easier way to deal with the cache by implementing the getter function in the parameter. When the cache is missing, it is going to refill the cache automatically.

funcExampleCache_GetByFunc() {
// We create a group of cache named "get-by-func".// It uses the local cache only with TTL of ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "get-by-func",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MarshalFunc: msgpack.Marshal, // msgpack is from "github.com/vmihailenco/msgpack/v5"UnmarshalFunc: msgpack.Unmarshal,
},
})
ctx:=context.TODO()
container2:=&Object{}
iferr:=c.GetByFunc(ctx, "get-by-func", "key2", container2, func() (interface{}, error) {
// The getter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key2" and get the value of Object{Str: "value2", Num: 2}returnObject{Str: "value2", Num: 2}, nil
}); err!=nil {
panic("not expected")
}
fmt.Println(container2) // Object{ Str: "value2", Num: 2}// Output:// &{value2 2}
}

MGetter is another approaching way to do this. Set this function durning registering the Setting.

funcExampleService_Create_mGetter() {
// We create a group of cache named "mgetter".// It uses both shared and local caches with separated TTL of one hour and ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "mgetter",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: time.Hour},
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MGetter: func(keys...string) (interface{}, error) {
// The MGetter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key3" and get the value of Object{Str: "value3", Num: 3}// HINT: remember to return as a slice, and the item order needs to consist with the keys in the parameters.return []Object{{Str: "value3", Num: 3}}, nil
},
MarshalFunc: cache.Marshal,
UnmarshalFunc: cache.Unmarshal,
},
})
ctx:=context.TODO()
container3:=&Object{}
iferr:=c.Get(ctx, "mgetter", "key3", container3); err!=nil {
panic("not expected")
}
fmt.Println(container3) // Object{ Str: "value3", Num: 3}// Output:// &{value3 3}
}

More examples

References

License

Apache-2.0

FOSSA Status

About

A flexible multi-layer Go caching library to deal with in-memory and shared cache by adopting Cache-Aside pattern.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - nicknameDZ/go-cache: A flexible multi-layer Go caching library to deal with in-memory and shared cache by adopting Cache-Aside pattern. · GitHub
Skip to content

Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

go-cache

Go DocBuild StatusGo Report CardcodecovCoverage StatusMaintainabilitySourcegraphLicenseFOSSA StatusMentioned in Awesome Go

Photo by Ashley McNamara, via ashleymcnamara/gophers (CC BY-NC-SA 4.0)

A flexible multi-layered caching library interacts with private (in-memory) cache and shared cache (i.e. Redis) in Go. It provides Cache-Aside strategy when dealing with both, and maintains the consistency of private cache between distributed systems by Pub-Sub pattern.

Caching is a common technique that aims to improve the performance and scalability of a system. It does this by temporarily copying frequently accessed data to fast storage close to the application. Distributed applications typically implement either or both of the following strategies when caching data:

  • Using a private cache, where data is held locally on the computer that's running an instance of an application or service.
  • Using a shared cache, serving as a common source that can be accessed by multiple processes and machines.

Using a local private cache with a shared cache Ref: https://docs.microsoft.com/en-us/azure/architecture/best-practices/images/caching/caching3.png

Considering the flexibility, efficiency and consistency, we starts to build up our own framework.

Features

  • Easy to use : provide a friendly interface to deal with both caching mechnaism by simple configuration. Limit the size of memory on single instance (pod) as well.
  • Maintain consistency : evict keys between distributed systems by Pub-Sub pattern.
  • Data compression : provide customized marshal and unmarshal functions.
  • Fix concurrency issue : prevent data racing happened on single instance (pod).
  • Metric : provide callback functions to measure the performance. (i.e. hit rate, private cache usage, ...)

Data flow

Load the cache with Cache-Aside strategy

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant R as Resource (Microservice / DB)
APP ->> M: Cache.Get() / Cache.MGet()
alt Local Cache hit
M ->> L: Adapter.MGet()
L -->> M: {[]Value, error}
M -->> APP: return
else Local Cache miss but Shared Cache hit
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: {[]Value, error}
M ->> L: Adapter.MSet()
M -->> APP: return
else All miss
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: cache miss
M ->> R: OneTimeGetterFunc() / MGetterFunc()
R -->> M: return from getter
M ->> S: Adapter.MSet()
M ->> L: Adapter.MSet()
M -->> APP: return
end
Loading

Evict the cache

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant PS as PubSub
APP ->> M: Cache.Del()
M ->> S: Adapter.Del()
S -->> M: return error if necessary
M ->> L: Adapter.Del()
L -->> M: return error if necessary
M ->> PS: Pubsub.Pub() (broadcast key eviction)
M -->> APP: return nil or error
Loading

Installation

go get github.com/viney-shih/go-cache

Get Started

Basic usage: Set-And-Get

By adopting Singleton pattern, initialize the Factory in main.go at the beginning, and deliver it to each package or business logic.

// Initialize the Factory in main.gotinyLfu:=cache.NewTinyLFU(10000)
rds:=cache.NewRedis(redis.NewRing(&redis.RingOptions{
Addrs: map[string]string{
"server1": ":6379",
},
}))
cacheFactory:=cache.NewFactory(rds, tinyLfu)

Treat it as a common key:value store like Redis. But more advanced, it coordinated the usage between multi-layered caching mechanism inside.

typeObjectstruct {
StrstringNumint
}
funcExample_setAndGetPattern() {
// We create a group of cache named "set-and-get".// It uses the shared cache only. Each key will be expired within ten seconds.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "set-and-get",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: 10*time.Second},
},
},
})
ctx:=context.TODO()
// set the cacheobj:=&Object{
Str: "value1",
Num: 1,
}
iferr:=c.Set(ctx, "set-and-get", "key", obj); err!=nil {
panic("not expected")
}
// read the cachecontainer:=&Object{}
iferr:=c.Get(ctx, "set-and-get", "key", container); err!=nil {
panic("not expected")
}
fmt.Println(container) // Output: Object{ Str: "value1", Num: 1}// read the cache but failediferr:=c.Get(ctx, "set-and-get", "no-such-key", container); err!=nil {
fmt.Println(err) // Output: errors.New("cache key is missing")
}
// Output:// &{value1 1}// cache key is missing
}

Advanced usage: Cache-Aside strategy

GetByFunc() is the easier way to deal with the cache by implementing the getter function in the parameter. When the cache is missing, it is going to refill the cache automatically.

funcExampleCache_GetByFunc() {
// We create a group of cache named "get-by-func".// It uses the local cache only with TTL of ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "get-by-func",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MarshalFunc: msgpack.Marshal, // msgpack is from "github.com/vmihailenco/msgpack/v5"UnmarshalFunc: msgpack.Unmarshal,
},
})
ctx:=context.TODO()
container2:=&Object{}
iferr:=c.GetByFunc(ctx, "get-by-func", "key2", container2, func() (interface{}, error) {
// The getter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key2" and get the value of Object{Str: "value2", Num: 2}returnObject{Str: "value2", Num: 2}, nil
}); err!=nil {
panic("not expected")
}
fmt.Println(container2) // Object{ Str: "value2", Num: 2}// Output:// &{value2 2}
}

MGetter is another approaching way to do this. Set this function durning registering the Setting.

funcExampleService_Create_mGetter() {
// We create a group of cache named "mgetter".// It uses both shared and local caches with separated TTL of one hour and ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "mgetter",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: time.Hour},
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MGetter: func(keys...string) (interface{}, error) {
// The MGetter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key3" and get the value of Object{Str: "value3", Num: 3}// HINT: remember to return as a slice, and the item order needs to consist with the keys in the parameters.return []Object{{Str: "value3", Num: 3}}, nil
},
MarshalFunc: cache.Marshal,
UnmarshalFunc: cache.Unmarshal,
},
})
ctx:=context.TODO()
container3:=&Object{}
iferr:=c.Get(ctx, "mgetter", "key3", container3); err!=nil {
panic("not expected")
}
fmt.Println(container3) // Object{ Str: "value3", Num: 3}// Output:// &{value3 3}
}

More examples

References

License

Apache-2.0

FOSSA Status

About

A flexible multi-layer Go caching library to deal with in-memory and shared cache by adopting Cache-Aside pattern.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - nicknameDZ/go-cache: A flexible multi-layer Go caching library to deal with in-memory and shared cache by adopting Cache-Aside pattern. · GitHub
Skip to content

Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

go-cache

Go DocBuild StatusGo Report CardcodecovCoverage StatusMaintainabilitySourcegraphLicenseFOSSA StatusMentioned in Awesome Go

Photo by Ashley McNamara, via ashleymcnamara/gophers (CC BY-NC-SA 4.0)

A flexible multi-layered caching library interacts with private (in-memory) cache and shared cache (i.e. Redis) in Go. It provides Cache-Aside strategy when dealing with both, and maintains the consistency of private cache between distributed systems by Pub-Sub pattern.

Caching is a common technique that aims to improve the performance and scalability of a system. It does this by temporarily copying frequently accessed data to fast storage close to the application. Distributed applications typically implement either or both of the following strategies when caching data:

  • Using a private cache, where data is held locally on the computer that's running an instance of an application or service.
  • Using a shared cache, serving as a common source that can be accessed by multiple processes and machines.

Using a local private cache with a shared cache Ref: https://docs.microsoft.com/en-us/azure/architecture/best-practices/images/caching/caching3.png

Considering the flexibility, efficiency and consistency, we starts to build up our own framework.

Features

  • Easy to use : provide a friendly interface to deal with both caching mechnaism by simple configuration. Limit the size of memory on single instance (pod) as well.
  • Maintain consistency : evict keys between distributed systems by Pub-Sub pattern.
  • Data compression : provide customized marshal and unmarshal functions.
  • Fix concurrency issue : prevent data racing happened on single instance (pod).
  • Metric : provide callback functions to measure the performance. (i.e. hit rate, private cache usage, ...)

Data flow

Load the cache with Cache-Aside strategy

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant R as Resource (Microservice / DB)
APP ->> M: Cache.Get() / Cache.MGet()
alt Local Cache hit
M ->> L: Adapter.MGet()
L -->> M: {[]Value, error}
M -->> APP: return
else Local Cache miss but Shared Cache hit
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: {[]Value, error}
M ->> L: Adapter.MSet()
M -->> APP: return
else All miss
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: cache miss
M ->> R: OneTimeGetterFunc() / MGetterFunc()
R -->> M: return from getter
M ->> S: Adapter.MSet()
M ->> L: Adapter.MSet()
M -->> APP: return
end
Loading

Evict the cache

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant PS as PubSub
APP ->> M: Cache.Del()
M ->> S: Adapter.Del()
S -->> M: return error if necessary
M ->> L: Adapter.Del()
L -->> M: return error if necessary
M ->> PS: Pubsub.Pub() (broadcast key eviction)
M -->> APP: return nil or error
Loading

Installation

go get github.com/viney-shih/go-cache

Get Started

Basic usage: Set-And-Get

By adopting Singleton pattern, initialize the Factory in main.go at the beginning, and deliver it to each package or business logic.

// Initialize the Factory in main.gotinyLfu:=cache.NewTinyLFU(10000)
rds:=cache.NewRedis(redis.NewRing(&redis.RingOptions{
Addrs: map[string]string{
"server1": ":6379",
},
}))
cacheFactory:=cache.NewFactory(rds, tinyLfu)

Treat it as a common key:value store like Redis. But more advanced, it coordinated the usage between multi-layered caching mechanism inside.

typeObjectstruct {
StrstringNumint
}
funcExample_setAndGetPattern() {
// We create a group of cache named "set-and-get".// It uses the shared cache only. Each key will be expired within ten seconds.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "set-and-get",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: 10*time.Second},
},
},
})
ctx:=context.TODO()
// set the cacheobj:=&Object{
Str: "value1",
Num: 1,
}
iferr:=c.Set(ctx, "set-and-get", "key", obj); err!=nil {
panic("not expected")
}
// read the cachecontainer:=&Object{}
iferr:=c.Get(ctx, "set-and-get", "key", container); err!=nil {
panic("not expected")
}
fmt.Println(container) // Output: Object{ Str: "value1", Num: 1}// read the cache but failediferr:=c.Get(ctx, "set-and-get", "no-such-key", container); err!=nil {
fmt.Println(err) // Output: errors.New("cache key is missing")
}
// Output:// &{value1 1}// cache key is missing
}

Advanced usage: Cache-Aside strategy

GetByFunc() is the easier way to deal with the cache by implementing the getter function in the parameter. When the cache is missing, it is going to refill the cache automatically.

funcExampleCache_GetByFunc() {
// We create a group of cache named "get-by-func".// It uses the local cache only with TTL of ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "get-by-func",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MarshalFunc: msgpack.Marshal, // msgpack is from "github.com/vmihailenco/msgpack/v5"UnmarshalFunc: msgpack.Unmarshal,
},
})
ctx:=context.TODO()
container2:=&Object{}
iferr:=c.GetByFunc(ctx, "get-by-func", "key2", container2, func() (interface{}, error) {
// The getter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key2" and get the value of Object{Str: "value2", Num: 2}returnObject{Str: "value2", Num: 2}, nil
}); err!=nil {
panic("not expected")
}
fmt.Println(container2) // Object{ Str: "value2", Num: 2}// Output:// &{value2 2}
}

MGetter is another approaching way to do this. Set this function durning registering the Setting.

funcExampleService_Create_mGetter() {
// We create a group of cache named "mgetter".// It uses both shared and local caches with separated TTL of one hour and ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "mgetter",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: time.Hour},
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MGetter: func(keys...string) (interface{}, error) {
// The MGetter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key3" and get the value of Object{Str: "value3", Num: 3}// HINT: remember to return as a slice, and the item order needs to consist with the keys in the parameters.return []Object{{Str: "value3", Num: 3}}, nil
},
MarshalFunc: cache.Marshal,
UnmarshalFunc: cache.Unmarshal,
},
})
ctx:=context.TODO()
container3:=&Object{}
iferr:=c.Get(ctx, "mgetter", "key3", container3); err!=nil {
panic("not expected")
}
fmt.Println(container3) // Object{ Str: "value3", Num: 3}// Output:// &{value3 3}
}

More examples

References

License

Apache-2.0

FOSSA Status

About

A flexible multi-layer Go caching library to deal with in-memory and shared cache by adopting Cache-Aside pattern.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - nicknameDZ/go-cache: A flexible multi-layer Go caching library to deal with in-memory and shared cache by adopting Cache-Aside pattern. · GitHub
Skip to content

Latest commit

History

32 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

go-cache

Go DocBuild StatusGo Report CardcodecovCoverage StatusMaintainabilitySourcegraphLicenseFOSSA StatusMentioned in Awesome Go

Photo by Ashley McNamara, via ashleymcnamara/gophers (CC BY-NC-SA 4.0)

A flexible multi-layered caching library interacts with private (in-memory) cache and shared cache (i.e. Redis) in Go. It provides Cache-Aside strategy when dealing with both, and maintains the consistency of private cache between distributed systems by Pub-Sub pattern.

Caching is a common technique that aims to improve the performance and scalability of a system. It does this by temporarily copying frequently accessed data to fast storage close to the application. Distributed applications typically implement either or both of the following strategies when caching data:

  • Using a private cache, where data is held locally on the computer that's running an instance of an application or service.
  • Using a shared cache, serving as a common source that can be accessed by multiple processes and machines.

Using a local private cache with a shared cache Ref: https://docs.microsoft.com/en-us/azure/architecture/best-practices/images/caching/caching3.png

Considering the flexibility, efficiency and consistency, we starts to build up our own framework.

Features

  • Easy to use : provide a friendly interface to deal with both caching mechnaism by simple configuration. Limit the size of memory on single instance (pod) as well.
  • Maintain consistency : evict keys between distributed systems by Pub-Sub pattern.
  • Data compression : provide customized marshal and unmarshal functions.
  • Fix concurrency issue : prevent data racing happened on single instance (pod).
  • Metric : provide callback functions to measure the performance. (i.e. hit rate, private cache usage, ...)

Data flow

Load the cache with Cache-Aside strategy

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant R as Resource (Microservice / DB)
APP ->> M: Cache.Get() / Cache.MGet()
alt Local Cache hit
M ->> L: Adapter.MGet()
L -->> M: {[]Value, error}
M -->> APP: return
else Local Cache miss but Shared Cache hit
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: {[]Value, error}
M ->> L: Adapter.MSet()
M -->> APP: return
else All miss
M ->> L: Adapter.MGet()
L -->> M: cache miss
M ->> S: Adapter.MGet()
S -->> M: cache miss
M ->> R: OneTimeGetterFunc() / MGetterFunc()
R -->> M: return from getter
M ->> S: Adapter.MSet()
M ->> L: Adapter.MSet()
M -->> APP: return
end
Loading

Evict the cache

sequenceDiagram
participant APP as Application
participant M as go-cache
participant L as Local Cache
participant S as Shared Cache
participant PS as PubSub
APP ->> M: Cache.Del()
M ->> S: Adapter.Del()
S -->> M: return error if necessary
M ->> L: Adapter.Del()
L -->> M: return error if necessary
M ->> PS: Pubsub.Pub() (broadcast key eviction)
M -->> APP: return nil or error
Loading

Installation

go get github.com/viney-shih/go-cache

Get Started

Basic usage: Set-And-Get

By adopting Singleton pattern, initialize the Factory in main.go at the beginning, and deliver it to each package or business logic.

// Initialize the Factory in main.gotinyLfu:=cache.NewTinyLFU(10000)
rds:=cache.NewRedis(redis.NewRing(&redis.RingOptions{
Addrs: map[string]string{
"server1": ":6379",
},
}))
cacheFactory:=cache.NewFactory(rds, tinyLfu)

Treat it as a common key:value store like Redis. But more advanced, it coordinated the usage between multi-layered caching mechanism inside.

typeObjectstruct {
StrstringNumint
}
funcExample_setAndGetPattern() {
// We create a group of cache named "set-and-get".// It uses the shared cache only. Each key will be expired within ten seconds.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "set-and-get",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: 10*time.Second},
},
},
})
ctx:=context.TODO()
// set the cacheobj:=&Object{
Str: "value1",
Num: 1,
}
iferr:=c.Set(ctx, "set-and-get", "key", obj); err!=nil {
panic("not expected")
}
// read the cachecontainer:=&Object{}
iferr:=c.Get(ctx, "set-and-get", "key", container); err!=nil {
panic("not expected")
}
fmt.Println(container) // Output: Object{ Str: "value1", Num: 1}// read the cache but failediferr:=c.Get(ctx, "set-and-get", "no-such-key", container); err!=nil {
fmt.Println(err) // Output: errors.New("cache key is missing")
}
// Output:// &{value1 1}// cache key is missing
}

Advanced usage: Cache-Aside strategy

GetByFunc() is the easier way to deal with the cache by implementing the getter function in the parameter. When the cache is missing, it is going to refill the cache automatically.

funcExampleCache_GetByFunc() {
// We create a group of cache named "get-by-func".// It uses the local cache only with TTL of ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "get-by-func",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MarshalFunc: msgpack.Marshal, // msgpack is from "github.com/vmihailenco/msgpack/v5"UnmarshalFunc: msgpack.Unmarshal,
},
})
ctx:=context.TODO()
container2:=&Object{}
iferr:=c.GetByFunc(ctx, "get-by-func", "key2", container2, func() (interface{}, error) {
// The getter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key2" and get the value of Object{Str: "value2", Num: 2}returnObject{Str: "value2", Num: 2}, nil
}); err!=nil {
panic("not expected")
}
fmt.Println(container2) // Object{ Str: "value2", Num: 2}// Output:// &{value2 2}
}

MGetter is another approaching way to do this. Set this function durning registering the Setting.

funcExampleService_Create_mGetter() {
// We create a group of cache named "mgetter".// It uses both shared and local caches with separated TTL of one hour and ten minutes.c:=cacheFactory.NewCache([]cache.Setting{
{
Prefix: "mgetter",
CacheAttributes: map[cache.Type]cache.Attribute{
cache.SharedCacheType: {TTL: time.Hour},
cache.LocalCacheType: {TTL: 10*time.Minute},
},
MGetter: func(keys...string) (interface{}, error) {
// The MGetter is used to generate data when cache missed, and refill the cache automatically..// You can read from DB or other microservices.// Assume we read from MySQL according to the key "key3" and get the value of Object{Str: "value3", Num: 3}// HINT: remember to return as a slice, and the item order needs to consist with the keys in the parameters.return []Object{{Str: "value3", Num: 3}}, nil
},
MarshalFunc: cache.Marshal,
UnmarshalFunc: cache.Unmarshal,
},
})
ctx:=context.TODO()
container3:=&Object{}
iferr:=c.Get(ctx, "mgetter", "key3", container3); err!=nil {
panic("not expected")
}
fmt.Println(container3) // Object{ Str: "value3", Num: 3}// Output:// &{value3 3}
}

More examples

References

License

Apache-2.0

FOSSA Status

About

A flexible multi-layer Go caching library to deal with in-memory and shared cache by adopting Cache-Aside pattern.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages