Skip to content

Instantly share code, notes, and snippets.

@pintsized
Last active April 19, 2017 21:21
Show Gist options
  • Save pintsized/4353981 to your computer and use it in GitHub Desktop.
Save pintsized/4353981 to your computer and use it in GitHub Desktop.
Ledge state machine idea
lsm_mod = require("lsm")
lsm = lsm_mod:new("checking_request_accepts_cache") -- Pass in initial state
-- ACTIONS ---------------------------------
lsm:action("redis_connect", function()
print("#a: Connecting to redis")
end)
lsm:action("redis_close", function()
print("#a: Closing redis connection")
end)
lsm:action("read_cache", function()
print("#a: Reading cache from Redis")
res = {
status = 200,
header = {
["Content-Type"] = "text/plain",
},
body = "Hello, from Cache"
}
end)
lsm:action("fetch", function()
print("#a: Fetching from origin")
if only_if_cached then
res.status = 504
else
res = { header = {}, status = 200, body = "Hello, from Origin" }
end
end)
lsm:action("remove_client_validators", function()
print("#a: Removing client validators")
end)
lsm:action("add_stale_warning", function()
print("#a: 110 Response is stale")
end)
lsm:action("serve", function()
print("#a: Serving")
print(res.status)
for f,v in pairs(res.header) do
print(f .. ": " .. v)
end
print(res.body)
end)
lsm:action("background_revalidate", function()
print("#a: Starting background revalidation")
end)
lsm:action("update_cache", function()
print("#a: Updating cache")
end)
-- BEFORE TRANS ------------------------
lsm:before_transition({ to = "checking_request_accepts_cache", action = "redis_connect" })
lsm:before_transition({ to = "exiting", action = "redis_close" })
lsm:before_transition({ to = "serving_stale", action = "add_stale_warning" })
-- EVENTS -------------------------------------
lsm:event("cache_accepted", {
{ when = "checking_request_accepts_cache", begin = "checking_cache", but_first = "read_cache" },
{ when = "validating_locally", begin = "serving" }
})
lsm:event("cache_not_accepted", {
{ when = "checking_request_accepts_cache", begin = "fetching" }
})
lsm:event("cache_missing", {
{ when = "checking_cache", begin = "fetching", but_first = "remove_client_validators" }
})
lsm:event("cache_expired", {
{ when = "checking_cache", begin = "checking_can_serve_stale" },
{ when = "checking_can_serve_stale", begin = "fetching", but_first = "remove_client_validators" }
})
lsm:event("cache_valid", {
{ when = "checking_cache", begin = "considering_revalidation" }
})
lsm:event("response_fetched", {
{ begin = "serving", but_first = "update_cache" }
})
lsm:event("must_revalidate", {
{ when = "considering_revalidation", begin = "considering_local_revalidation" },
{ when = "considering_local_revalidation", begin = "revalidating_upstream" },
})
lsm:event("can_revalidate_locally", {
{ begin = "revalidating_locally" },
})
lsm:event("not_modified", {
{ when = "revalidating_locally", begin = "serving_not_modified" },
{ when = "re_revalidating_locally", begin = "serving_not_modified" },
{ when = "revalidating_upstream", begin = "re_revalidating_locally" },
{ when = "revalidating_in_background", begin = "exiting" },
})
lsm:event("modified", {
{ when = "revalidating_locally", begin = "revalidating_upstream" },
{ when = "re_revalidating_locally", begin = "serving" },
{ when = "revalidating_upstream", begin = "re_revalidating_locally", but_first = "update_cache" },
{ when = "revalidating_in_background", begin = "exiting", but_first = "update_cache" }
})
lsm:event("serve_stale", {
{ when = "checking_can_serve_stale", begin = "serving_stale" }
})
lsm:event("served", {
{ when = "serving_stale", begin = "revalidating_in_background" },
{ begin = "exiting" },
})
-- STATES ------------------------------
lsm:state("checking_request_accepts_cache", function()
local req = req()
if req.cache_accepted == true then
return lsm:cache_accepted()
else
return lsm:cache_not_accepted()
end
end)
lsm:state("checking_cache", function()
local res = read()
if not res then
return lsm:cache_missing()
elseif res.expired then
return lsm:cache_expired()
else
return lsm:cache_valid()
end
end)
lsm:state("fetching", function()
local res = fetch()
if res.status == 304 then
return lsm:can_serve()
else
return lsm:response_fetched()
end
end)
lsm:state("considering_revalidation", function()
return lsm:must_revalidate()
end)
lsm:state("considering_local_revalidation", function()
return lsm:can_revalidate_locally()
end)
lsm:state("revalidating_locally", function()
return lsm:modified()
end)
lsm:state("re_revalidating_locally", function()
return lsm:not_modified()
--return lsm:modified()
end)
lsm:state("revalidating_upstream", function()
return lsm:modified()
--return lsm:not_modified()
end)
lsm:state("revalidating_in_background", function()
-- return lsm:modified()
return lsm:not_modified()
end)
lsm:state("checking_can_serve_stale", function()
return lsm:serve_stale()
--return lsm:cache_expired()
end)
lsm:state("serving", function()
return lsm:served()
end)
lsm:state("serving_not_modified", function()
return lsm:served()
end)
lsm:state("serving_stale", function()
return lsm:served()
end)
lsm:state("exiting", function()
end)
-- Go
module("lsm", package.seeall)
local mt = {
-- Expose all events as functions
__index = function(t, k)
if t.events[k] then
return t.events[k]
else
return _M[k]
end
end
}
function new(self, initial)
return setmetatable({
current_state = nil,
initial_state = initial,
events = {},
states = {},
actions = {},
before_transitions = {},
}, mt)
end
function event(self, ev, transition_table)
assert(not self.events[ev], "#e: Cannot redefine event '" .. ev .. "'")
self.events[ev] = function()
for _,trans in ipairs(transition_table) do
if trans["when"] == nil or trans["when"] == self.current_state then
assert(trans["begin"], "#e: Nothing to begin after '" .. ev .. "' when '" .. self.current_state .. "'")
print("#e: " .. ev)
if trans["but_first"] then
assert("function" == type(self.actions[trans["but_first"]]), "No action function defined for '" .. trans["but_first"] .. "'")
self.actions[trans["but_first"]]()
end
return self:transition(trans["begin"])
end
end
print("#e: ERROR: No transitions found for " .. ev .. " when " .. self.current_state)
end
end
function state(self, st, func)
self.states[st] = func
end
function action(self, ac, func)
self.actions[ac] = func
end
function before_transition(self, rule)
self.before_transitions[rule["to"]] = { from = rule["from"], action = rule["action"] }
end
function transition(self, state)
assert("function" == type(self.states[state]), "State '" .. state .. "' function not defined")
-- Check for any transition pre-tasks
local before_t = self.before_transitions[state]
if before_t and (before_t["from"] == nil or self.current_state == before_t["from"]) then
assert("function" == type(self.actions[before_t["action"]]),
"Action " .. before_t["action"] .. " is not a function")
self.actions[before_t["action"]]()
end
print("#t: " .. state)
self.current_state = state
return self.states[self.current_state]()
end
function start(self)
self:transition(self.initial_state)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment