Skip to content

Commit

Permalink
Merge 8196ebe into e6f20c0
Browse files Browse the repository at this point in the history
  • Loading branch information
QuangTung97 committed Nov 28, 2023
2 parents e6f20c0 + 8196ebe commit f19fd33
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 0 deletions.
10 changes: 10 additions & 0 deletions README.md
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)

Binary file added docs/images/cache-aside.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
71 changes: 71 additions & 0 deletions docs/overview.md
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
```

0 comments on commit f19fd33

Please sign in to comment.