-
Notifications
You must be signed in to change notification settings - Fork 1
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
Showing
3 changed files
with
81 additions
and
0 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 |
---|---|---|
@@ -1,3 +1,13 @@ | ||
# A Caching Library with Strong Consistency | ||
|
||
[![memproxy-py](https://github.com/QuangTung97/memproxy-py/actions/workflows/go.yml/badge.svg)](https://github.com/QuangTung97/memproxy-py/actions/workflows/go.yml) | ||
[![Coverage Status](https://coveralls.io/repos/github/QuangTung97/memproxy-py/badge.svg?branch=master)](https://coveralls.io/github/QuangTung97/memproxy-py?branch=master) | ||
|
||
## Installation | ||
```shell | ||
pip install memproxy==0.3.0rc20 | ||
``` | ||
|
||
## Design Documentation | ||
1. [Overview](docs/overview.md) | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# Architecture Overview | ||
|
||
![Cache Aside Pattern](images/cache-aside.png) | ||
|
||
This library developed using the **Cache-Aside Pattern**, with basic steps: | ||
1. Data is first get from Cache Server (Redis) | ||
2. If Data is Existed on the Cache => Returns to Client | ||
3. If Data is NOT Existed => Get From DB => Set Back to Redis => Returns to Client | ||
|
||
Why **Cache-Aside**? | ||
1. Easier to implement, can do in a way that guarantees consistency under any circumstances. | ||
2. Recommended way of Caching Things. | ||
3. Client SHOULD use deletion to do invalidation, because it can do in a generic way by \ | ||
using [Before Commit Hook](https://docs.sqlalchemy.org/en/13/orm/events.html#sqlalchemy.orm.events.SessionEvents.before_commit) and [After Commit Hook](https://docs.sqlalchemy.org/en/13/orm/events.html#sqlalchemy.orm.events.SessionEvents.after_commit) | ||
combining with the **Transactional Outbox Pattern**. | ||
4. Can use the LRU Mechanism ([Redis LRU](https://redis.io/docs/reference/eviction/)) to NOT have to worry about memory limit | ||
|
||
## How this Library guarantee consistency between Redis and Database | ||
Based on [Memcache Lease](https://research.facebook.com/publications/scaling-memcache-at-facebook/) \ | ||
In order to implement this algorithm, this library used **Lua Scripting**. | ||
|
||
With 2 Scripts Below: | ||
|
||
### Script 1: Get Data from Cache | ||
``` | ||
local result = {} | ||
for i = 1,#KEYS do | ||
local k = KEYS[i] | ||
local resp = redis.call('GET', k) | ||
if resp then | ||
result[i] = resp | ||
else | ||
local cas = redis.call('INCR', '__next_cas') | ||
local cas_str = 'cas:' .. cas | ||
redis.call('SET', k, cas_str, 'EX', 3) | ||
result[i] = cas_str | ||
end | ||
end | ||
return result | ||
``` | ||
|
||
|
||
### Script 2: Set Data to Cache | ||
``` | ||
local result = {} | ||
for i = 1,#KEYS do | ||
local k = KEYS[i] | ||
local resp = redis.call('GET', k) | ||
local cas_str = 'cas:' .. ARGV[i * 3 - 2] | ||
local val = 'val:' .. ARGV[i * 3 - 1] | ||
local ttl = ARGV[i * 3] | ||
if not resp then | ||
result[i] = 'NF' | ||
elseif resp ~= cas_str then | ||
result[i] = 'EX' | ||
else | ||
redis.call('SET', k, val, 'EX', ttl) | ||
result[i] = 'OK' | ||
end | ||
end | ||
return result | ||
``` |