-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
anonyindian
committed
Feb 28, 2023
1 parent
b7626b8
commit 234c4c0
Showing
17 changed files
with
585 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,3 +13,5 @@ | |
|
||
# Dependency directories (remove the comment below to include it) | ||
# vendor/ | ||
|
||
benchfiles/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,96 @@ | ||
# Cacher | ||
Cacher is a fast, decentralised caching system, generic in nature and uses Go's in built mapping to store data. It has a plenty of features like TTL, Revaluation etc. | ||
# Cacher | ||
|
||
Some of the main features are descried below: | ||
Cacher is a fast, decentralised caching library, generic in nature and uses Go's built-in maps to store data. It has a plenty of features like TTL, Revaluation etc. | ||
|
||
TTL (Time-To-Live): It allows us to expire a key after a specific time period. | ||
There are plenty examples available in the [examples](./examples) directory and cover almost every feature of the library with explanation and real life examples. | ||
|
||
Revaluation: This is another useful feature that allows us to keep keys cached as per their usage frequency. | ||
Support Go Versions: Go v1.18 and newer | ||
|
||
[![Go Reference](https://pkg.go.dev/badge/github.com/animekaizoku/cacher.svg)](https://pkg.go.dev/github.com/animekaizoku/cacher) [![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](http://perso.crans.org/besson/LICENSE.html) | ||
|
||
## Key Highlights: | ||
- **Decentralised**: Allows users to implement decentralised caching system which helps to perform multiple set-get operations parallely. | ||
- **TTL (Time-To-Live)**: It allows us to expire a key after a specific time period. | ||
- **Revaluation**: This is another useful feature that allows us to keep keys cached as per their usage frequency. | ||
- **Zero Bloat**: Doesn't rely on any 3rd party library and only uses standard ones. | ||
- **Structs Friendly**: You don't need to serialize your structs to bytes to save them as values, which makes the set-get process faster and allows us to write more readable code. | ||
- **Well Documentated**: Cacher is very well documentated and contains examples in the docs wherever required. There are plenty of examples available in the [examples](./examples) directory to get a quick overview. | ||
|
||
## Getting Started | ||
We have divided this section in several sub-sections, you can follow them sequentially to start using this library right now! | ||
|
||
### Downloading the library | ||
You can download it using the standard `go get` command: | ||
```bash | ||
go get github.com/AnimeKaizoku/cacher | ||
``` | ||
|
||
### Basic Example | ||
We will assume that you've already downloaded the library in your project as described in the previous section. | ||
Now we will learn how to use it in your Go projects with the help of a basic example. | ||
|
||
You can check-out examples directory for more detailed and well explained examples, here we will create just a basic program with a simple Cacher instance: | ||
```golang | ||
package main | ||
|
||
import "github.com/AnimeKaizoku/cacher" | ||
|
||
var cache = cacher.NewCacher[int, string](nil) | ||
|
||
func main() { | ||
cache.Set(1, "Value for 1 of type string") | ||
cache.Set(2, "Value for 2 of type string") | ||
|
||
valueOf1, ok := cache.Get(1) | ||
if !ok { | ||
println("value of 1 not found") | ||
return | ||
} | ||
println("value of 1 is:") | ||
println(valueOf1) | ||
} | ||
``` | ||
|
||
### Documentation and Examples | ||
Cacher is a well-documentated library, and contains well explained examples with the help of real life cases which can be found out in [examples](./examples/) directory. | ||
|
||
[Click here](https://pkg.go.dev/github.com/animekaizoku/cacher) to check-out documentations. | ||
|
||
### Decentralised Usage | ||
As we have already learnt how to create a simple Cacher instance, we can head towards our next step. | ||
|
||
Decentralising means, distributing the work of a single central system to multiple systems. Since Cacher is a lightweight library, we can easily create new cacher instances for every level of our program. | ||
|
||
An example structure is shown below: | ||
``` | ||
cacher1----module1.go---\ | ||
cacher2----module2.go----\ | ||
cacher3----module2.go-----} main.go | ||
cacher4----module3.go----/ | ||
cacher5----module4.go---/ | ||
``` | ||
As we can see above, we have 5 modules in our program (module 1-5) where each of them have their own Cacher instance instead of a single caching instance we would've defined for our whole program. This approach allows us to distribute our load to multiple cachers which will help us to achieve better efficiency than we would have got from a single central cacher. This approach also allows us to use different Types of values for different modules. | ||
|
||
## Benchmarks | ||
We made comparision between 3 libraries, i.e. Cacher, BigCache, FreeCache and took benchmark tests in two different situations: | ||
|
||
**Situation 1**: Serialisation needed | ||
![bench1](./assets/bench1.jpg) | ||
Above screenshot contains benchmark of the situation when you'll need to serialise your struct to bytes to store in BigCache/FreeCache, while you won't need to do that in case of Cacher since it uses Generics. | ||
We used "encoding/gob" for serialising the value struct to bytes | ||
|
||
|
||
**Situation 2**: Serialisation not needed | ||
![bench2](./assets/bench2.jpg) | ||
Above screenshot contains benchmark of the situation when you won't need to serialise your value to bytes or we can say, serialisation time removed. | ||
|
||
**Conclusion**: We can clearly see the difference. Although the libraries like BigCache and FreeCache act quickly without the serialisation time included, in practical life, you will **need to** serialise your date when attempting to cache structs while since Cacher uses Generic ability of Go, you won't need to serialise your data while using it and can just create a new cacher instance with your Value Type set as your struct's type. | ||
|
||
## Contributing | ||
Contributions are always welcome! Just create a PR (Pull Request) with what you want to contribute with a brief description. | ||
|
||
Please make sure to update examples as appropriate. | ||
|
||
## License | ||
[![GPLv3](https://www.gnu.org/graphics/gplv3-127x51.png)](https://www.gnu.org/licenses/gpl-3.0.en.html) | ||
<br>Cacher is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.en.html">GNU General Public License v3</a> |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
module github.com/AnimeKaizoku/cacher/examples/multiKey | ||
|
||
go 1.18 | ||
|
||
require github.com/AnimeKaizoku/cacher v0.0.0 | ||
|
||
replace github.com/AnimeKaizoku/cacher => ../../ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/AnimeKaizoku/cacher" | ||
) | ||
|
||
// We'll be using PolyKeyer hence we need to choose string as the data type for Key. | ||
// In this example, we won't be expiring keys and hence we don't pass them to opts. | ||
var cache = cacher.NewCacher[string, string](nil) | ||
|
||
// Following code initiates a PolyKeyer instance. | ||
// We've chosen "chat" as the prefix key (also called primary key) and 2 as our secondary | ||
// parameters. | ||
// Note that it'll panic in case you pass different number of secondary keys to the keyer.New | ||
// method than the chosen one while initiation. | ||
var keyer = cacher.NewPolyKeyer("chat", 2) | ||
|
||
// This function will call Get method on input key and print it if found. | ||
func get(key string) { | ||
title, ok := cache.Get(key) | ||
if !ok { | ||
fmt.Printf("Key '%s' not found in the valid cache!\n", key) | ||
return | ||
} | ||
fmt.Println("Title of that chat is:", title) | ||
} | ||
|
||
// This function will fetch all the keys in our current cacher and print them. | ||
func getAll() { | ||
allKeys := cache.GetAll() | ||
fmt.Println("All keys:") | ||
for _, x := range allKeys { | ||
fmt.Println("-", x) | ||
} | ||
} | ||
|
||
func main() { | ||
// We create a new key with exactly 2 arguments (we chose while initialising polykeyer) | ||
// Let's assume that our 1st argument will be chat id and 2nd argument can either be | ||
// "public" or "private". | ||
var key = keyer.New("101", "private") | ||
// Here we set the value for our key we created above and let our value be the title | ||
// of chat. | ||
cache.Set(key, "King's Chat") | ||
|
||
// Uncomment the following line of code to see how does a poly key look like actually: | ||
// fmt.Println("This is how a polykey look like:", key) | ||
|
||
// Let's retrieve our key | ||
// We'll use the get function we wrote earlier in this example for it! | ||
get(key) | ||
|
||
// We can know the number of keys which are present in our current | ||
// Cacher instance using the cacher.NumKeys() method. | ||
fmt.Println("Number of keys in current cacher:", cache.NumKeys()) | ||
|
||
// Resetting the cache will delete all key-value mapping present in | ||
// it currently. | ||
cache.Reset() | ||
|
||
// Let's try to get our key now, it should not be found as we recently | ||
// reset our cache. | ||
get(key) | ||
|
||
// Let's add the key again and a few more for further tutorial. | ||
cache.Set(key, "King's Chat") | ||
cache.Set(keyer.New("102", "public"), "Bob's chitchat group") | ||
cache.Set(keyer.New("103", "private"), "Cacher Test Chat") | ||
|
||
key1 := keyer.New("104", "public") | ||
cache.Set(key1, "Github Public Chat") | ||
|
||
cache.Set(keyer.New("105", "private"), "King Hero") | ||
|
||
// Let's print the number of keys now: | ||
fmt.Println("Number of keys:", cache.NumKeys()) | ||
|
||
// Now we will learn how to make a segrigator function which we can | ||
// use for doing ___Some calls which are conditional in nature. | ||
// Let out condition be: return true for all values which start | ||
// with "King" | ||
exampleSegrigator := func(v string) bool { | ||
// We'll use HasPrefix function from strings package determining | ||
// prefix. | ||
return strings.HasPrefix(v, "King") | ||
} | ||
|
||
// We can also get all or some particular keys in an array at once. | ||
// Following is the code on how to get some keys. | ||
values := cache.GetSome(exampleSegrigator) | ||
|
||
// Let's print our conditional values | ||
fmt.Println(`Here are chats which start with the phrase "King"`) | ||
for _, x := range values { | ||
fmt.Println("-", x) | ||
} | ||
|
||
// Now we'll learn how to delete a specific key from our current cacher | ||
// Let's suppose we want to delete key with chatid 104 and "public" | ||
// secondary keys, we'll use the key1 variable we defined earlier for | ||
// the purpose. | ||
cache.Delete(key1) | ||
|
||
// Let's print all the keys currently present in the cacher | ||
// We'll use getAll function we created earlier in this example for it. | ||
getAll() | ||
|
||
// We've learnt a lot till yet, it's the time we introduce ourselves to | ||
// the DeleteSome method, similar to GetSome, it deletes keys on the basis | ||
// of segrigations, we'll just use the previous segrigator function and delete | ||
// all keys which start with the phrase "King" | ||
cache.DeleteSome(exampleSegrigator) | ||
|
||
// Now we will again use getAll to see the difference finally | ||
getAll() | ||
} | ||
|
||
// Congratulations! You just learnt most of the usable methods of this library! | ||
// We request you to check out Single Key System examples to learn how to make | ||
// a cacher which will expire keys and that too with the revaluation mode. |
Oops, something went wrong.