Skip to content

Commit

Permalink
Modified plugin to cache to Redis instead of memory
Browse files Browse the repository at this point in the history
  • Loading branch information
ligreman committed Apr 16, 2021
1 parent a27c3a4 commit aa8940b
Show file tree
Hide file tree
Showing 14 changed files with 728 additions and 1,141 deletions.
41 changes: 34 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
[![Build Status][badge-travis-image]][badge-travis-url]
# Kong proxy-cache-redis plugin

# Kong proxy-cache plugin

HTTP Proxy Caching for Kong
HTTP Proxy Redis Caching for Kong

## Synopsis

Expand All @@ -14,9 +12,38 @@ to the same resource will re-fetch and re-store the resource. Cache entities
can also be forcefully purged via the Admin API prior to their expiration
time.

It caches all responses in a Redis server.

## Cache TTL

TTL for serving the cached data. Kong sends a `X-Cache-Status` with value `Refresh` if the resource was found in cache, but could not satisfy the request, due to Cache-Control behaviors or reaching its hard-coded cache_ttl threshold.

## Storage TTL
Kong can store resource entities in the storage engine longer than the prescribed cache_ttl or Cache-Control values indicate. This allows Kong to maintain a cached copy of a resource past its expiration. This allows clients capable of using max-age and max-stale headers to request stale copies of data if necessary.

## Documentation

* [Documentation for the Proxy Cache plugin](https://docs.konghq.com/hub/kong-inc/proxy-cache/)
The plugin works in the same way as the official `proxy-cache` plugin, in terms of the way it generates the cache key, or how to assign it to a service or route. [Documentation for the Proxy Cache plugin](https://docs.konghq.com/hub/kong-inc/proxy-cache/)

## Configuration

[badge-travis-url]: https://travis-ci.com/Kong/kong-plugin-proxy-cache/branches
[badge-travis-image]: https://travis-ci.com/Kong/kong-plugin-proxy-cache.svg?token=BfzyBZDa3icGPsKGmBHb&branch=master
|Parameter|Type|Required|Default|Description|
|---|---|---|---|---|
`name`|string|*required*| |The name of the plugin to use, in this case: `proxy-cache-redis`
`service.id`|string|*optional*| |The ID of the Service the plugin targets.
`route.id`|string|*optional*| |The ID of the Route the plugin targets.
`consumer.id`|string|*optional*| |The ID of the Consumer the plugin targets.
`enabled`|boolean|*optional*|true|Whether this plugin will be applied.
`config.response_code`|array of integers|*required*|[200, 301, 404]|Upstream response status code considered cacheable.
`config.request_method`|array of strings|*required*|["GET","HEAD"]|Downstream request methods considered cacheable.
`config.content_type`|array of strings|*required*|["text/plain", "application/json"]|Upstream response content types considered cacheable. The plugin performs an exact match against each specified value; for example, if the upstream is expected to respond with an application/json; charset=utf-8 content-type, the plugin configuration must contain said value or a Bypass cache status is returned.
`config.vary_headers`|array of strings|*optional*| |Relevant headers considered for the cache key. If undefined, none of the headers are taken into consideration.
`config.vary_query_params`|array of strings|*optional*| |Relevant query parameters considered for the cache key. If undefined, all params are taken into consideration.
`config.cache_ttl`|integer|*required*|300|TTL, in seconds, of cache resources.
`config.cache_control`|boolean|*required*|false|When enabled, respect the Cache-Control behaviors defined in RFC7234.
`config.storage_ttl`|integer|*required*| |Number of seconds to keep resources in the storage backend. This value is independent of cache_ttl or resource TTLs defined by Cache-Control behaviors. The resources may be stored for up to `storage_ttl` secs but served only for `cache_ttl`.
`config.redis_host`|string|*required*| |The hostname or IP address of the redis server.
`config.redis_port`|integer|*optional*|6379|The port of the redis server.
`config.redis_timeout`|integer|*optional*|2000|The timeout in milliseconds for the redis connection.
`config.redis_password`|string|*optional*| |The password (if required) to authenticate to the redis server.
`config.redis_database`|string|*optional*|0|The Redis database to use for caching the resources.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package = "kong-proxy-cache-redis-redis-plugin"
version = "1.3.1-1"
package = "kong-proxy-cache-redis-plugin"
version = "1.0.0-0"

source = {
url = "git://github.com/ligreman/kong-proxy-cache-redis-redis-plugin"
url = "git://github.com/ligreman/kong-proxy-cache-redis-plugin"
}

supported_platforms = {"linux", "macosx"}
Expand All @@ -23,7 +23,6 @@ build = {
["kong.plugins.proxy-cache-redis.cache_key"] = "kong/plugins/proxy-cache-redis/cache_key.lua",
["kong.plugins.proxy-cache-redis.schema"] = "kong/plugins/proxy-cache-redis/schema.lua",
["kong.plugins.proxy-cache-redis.api"] = "kong/plugins/proxy-cache-redis/api.lua",
["kong.plugins.proxy-cache-redis.strategies"] = "kong/plugins/proxy-cache-redis/strategies/init.lua",
["kong.plugins.proxy-cache-redis.strategies.memory"] = "kong/plugins/proxy-cache-redis/strategies/memory.lua",
["kong.plugins.proxy-cache-redis.redis"] = "kong/plugins/proxy-cache-redis/redis.lua",
}
}
242 changes: 67 additions & 175 deletions kong/plugins/proxy-cache-redis/api.lua
Original file line number Diff line number Diff line change
@@ -1,198 +1,90 @@
local STRATEGY_PATH = "kong.plugins.proxy-cache.strategies"


local require = require
local redis = require "kong.plugins.proxy-cache-redis.redis"
local kong = kong
local fmt = string.format


local function broadcast_purge(plugin_id, cache_key)
local data = fmt("%s:%s", plugin_id, cache_key or "nil")
kong.log.debug("broadcasting purge '", data, "'")
return kong.cluster_events:broadcast("proxy-cache:purge", data)
end


local function each_proxy_cache()
local iter = kong.db.plugins:each()

return function()
while true do
local plugin, err = iter()
if err then
return kong.response.exit(500, { message = err })
end
if not plugin then
return
end
if plugin.name == "proxy-cache" then
return plugin
end
end
end
end


return {
["/proxy-cache-redis"] = {
resource = "proxy-cache-redis",
["/plugins/:plugin_id/proxy-cache-redis"] = {

DELETE = function()
for plugin in each_proxy_cache() do
DELETE = function(self)
-- Busco el plugin
local plugin, errp = kong.db.plugins:select({ id = self.params.plugin_id })

local strategy = require(STRATEGY_PATH)({
strategy_name = plugin.config.strategy,
strategy_opts = plugin.config[plugin.config.strategy],
})

local ok, err = strategy:flush(true)
if not ok then
return kong.response.exit(500, { message = err })
end

if require(STRATEGY_PATH).LOCAL_DATA_STRATEGIES[plugin.config.strategy]
then
local ok, err = broadcast_purge(plugin.id, nil)
if not ok then
kong.log.err("failed broadcasting proxy cache purge to cluster: ", err)
end
end
if errp then
kong.log.err("Error retrieving the plugin: " .. errp)
return nil
end

end
if not plugin then
kong.log.err("Could not find plugin.")
return nil
end

return kong.response.exit(204)
end
},
["/proxy-cache-redis/:cache_key"] = {
resource = "proxy-cache-redis",
local ok, err = redis:flush(plugin.config)
if not ok then
return kong.response.exit(500, { message = err })
end

GET = function(self)
for plugin in each_proxy_cache() do
return kong.response.exit(204)
end
},
["/plugins/:plugin_id/proxy-cache-redis/:cache_key"] = {

local strategy = require(STRATEGY_PATH)({
strategy_name = plugin.config.strategy,
strategy_opts = plugin.config[plugin.config.strategy],
})
GET = function(self)
-- Busco el plugin
local plugin, errp = kong.db.plugins:select({ id = self.params.plugin_id })

local cache_val, err = strategy:fetch(self.params.cache_key)
if err and err ~= "request object not in cache" then
return kong.response.exit(500, err)
end
if errp then
kong.log.err("Error retrieving the plugin: " .. errp)
return nil
end

if cache_val then
return kong.response.exit(200, cache_val)
end
if not plugin then
kong.log.err("Could not find plugin.")
return nil
end

end
local cache_val, err = redis:fetch(plugin.config, self.params.cache_key)
if err and err ~= "request object not in cache" then
return kong.response.exit(500, err)
end

-- fell through, not found
return kong.response.exit(404)
end,
if cache_val then
return kong.response.exit(200, cache_val)
end

DELETE = function(self)
for plugin in each_proxy_cache() do
-- fell through, not found
return kong.response.exit(404)
end,

local strategy = require(STRATEGY_PATH)({
strategy_name = plugin.config.strategy,
strategy_opts = plugin.config[plugin.config.strategy],
})
DELETE = function(self)
-- Busco el plugin
local plugin, errp = kong.db.plugins:select({ id = self.params.plugin_id })

local cache_val, err = strategy:fetch(self.params.cache_key)
if err and err ~= "request object not in cache" then
return kong.response.exit(500, err)
end
if errp then
kong.log.err("Error retrieving the plugin: " .. errp)
return nil
end

if cache_val then
local _, err = strategy:purge(self.params.cache_key)
if err then
return kong.response.exit(500, err)
end
if not plugin then
kong.log.err("Could not find plugin.")
return nil
end

if require(STRATEGY_PATH).LOCAL_DATA_STRATEGIES[plugin.config.strategy]
then
local ok, err = broadcast_purge(plugin.id, self.params.cache_key)
if not ok then
kong.log.err("failed broadcasting proxy cache purge to cluster: ", err)
local cache_val, err = redis:fetch(plugin.config, self.params.cache_key)
if err and err ~= "request object not in cache" then
return kong.response.exit(500, err)
end
end

return kong.response.exit(204)
end
if cache_val then
local _, err2 = redis:purge(plugin.config, self.params.cache_key)
if err2 then
return kong.response.exit(500, err2)
end

end

-- fell through, not found
return kong.response.exit(404)
end,
},
["/proxy-cache-redis/:plugin_id/caches/:cache_key"] = {
resource = "proxy-cache-redis",

GET = function(self)
local plugin, err = kong.db.plugins:select {
id = self.params.plugin_id,
}
if err then
return kong.response.exit(500, err)
end

if not plugin then
return kong.response.exit(404)
end

local conf = plugin.config
local strategy = require(STRATEGY_PATH)({
strategy_name = conf.strategy,
strategy_opts = conf[conf.strategy],
})

local cache_val, err = strategy:fetch(self.params.cache_key)
if err == "request object not in cache" then
return kong.response.exit(404)
elseif err then
return kong.response.exit(500, err)
end

return kong.response.exit(200, cache_val)
end,
DELETE = function(self)
local plugin, err = kong.db.plugins:select {
id = self.params.plugin_id,
}
if err then
return kong.response.exit(500, err)
end

if not plugin then
return kong.response.exit(404)
end

local conf = plugin.config
local strategy = require(STRATEGY_PATH)({
strategy_name = conf.strategy,
strategy_opts = conf[conf.strategy],
})

local _, err = strategy:fetch(self.params.cache_key)
if err == "request object not in cache" then
return kong.response.exit(404)
elseif err then
return kong.response.exit(500, err)
end

local _, err = strategy:purge(self.params.cache_key)
if err then
return kong.response.exit(500, err)
end

if require(STRATEGY_PATH).LOCAL_DATA_STRATEGIES[conf.strategy] then
local ok, err = broadcast_purge(plugin.id, self.params.cache_key)
if not ok then
kong.log.err("failed broadcasting proxy cache purge to cluster: ", err)
end
end
return kong.response.exit(204)
end

return kong.response.exit(204)
end
},
-- fell through, not found
return kong.response.exit(404)
end,
}
}
Loading

0 comments on commit aa8940b

Please sign in to comment.