From f3ccfc158c4bc35322301f5f45c2912cdb23b9a9 Mon Sep 17 00:00:00 2001 From: Thibault Charbonnier Date: Mon, 2 Oct 2017 17:40:46 -0700 Subject: [PATCH] fix(postgres) ensure APIs created_at has ms precision APIs are retrieved without an `ORDER BY` clause from the datastore when building the router. Because of the lack of such a clause in our current Cassandra schema, we wish to implement sorting by creation date in our business logic, right before building the router. However, the PostgreSQL default clause of the APIs `created_at` field is set to `CURRENT_TIMESTAMP(0)`, which strips ms precision out of the timestamp. This changes the default timestamp precision to 3 digits so our sorting attribute (`created_at`) can be meaningful. This fix is targeting 0.11.1, and as such does not introduce a new migration to update the default value of `created_at`. This fix will only have effect for new database schemas. A second commit will introduce a migration, which, as per our policy, will be targeted for 0.12.0. Fix #2899 --- kong/core/handler.lua | 4 ++ kong/dao/db/postgres.lua | 6 +-- kong/dao/migrations/postgres.lua | 2 +- .../05-proxy/01-router_spec.lua | 54 +++++++++++++++++++ 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/kong/core/handler.lua b/kong/core/handler.lua index 9be13efe681..4f4108c4c40 100644 --- a/kong/core/handler.lua +++ b/kong/core/handler.lua @@ -51,6 +51,10 @@ local function build_router(dao, version) end end + table.sort(apis, function(api_a, api_b) + return api_a.created_at < api_b.created_at + end) + router, err = Router.new(apis) if not router then return nil, "could not create router: " .. err diff --git a/kong/dao/db/postgres.lua b/kong/dao/db/postgres.lua index ef3ab04fd42..d414eba728d 100644 --- a/kong/dao/db/postgres.lua +++ b/kong/dao/db/postgres.lua @@ -346,7 +346,7 @@ local function deserialize_timestamps(self, row, schema) for k, v in pairs(schema.fields) do if v.type == "timestamp" and result[k] then local query = fmt([[ - SELECT (extract(epoch from timestamp '%s')*1000)::bigint as %s; + SELECT (extract(epoch from timestamp '%s') * 1000) as %s; ]], result[k], k) local res, err = self:query(query) if not res then return nil, err @@ -363,8 +363,8 @@ local function serialize_timestamps(self, tbl, schema) for k, v in pairs(schema.fields) do if v.type == "timestamp" and result[k] then local query = fmt([[ - SELECT to_timestamp(%d/1000) at time zone 'UTC' as %s; - ]], result[k], k) + SELECT to_timestamp(%f) at time zone 'UTC' as %s; + ]], result[k] / 1000, k) local res, err = self:query(query) if not res then return nil, err elseif #res <= 1 then diff --git a/kong/dao/migrations/postgres.lua b/kong/dao/migrations/postgres.lua index cebc99f8377..24923fa46de 100644 --- a/kong/dao/migrations/postgres.lua +++ b/kong/dao/migrations/postgres.lua @@ -42,7 +42,7 @@ return { strip_request_path boolean NOT NULL, upstream_url text, preserve_host boolean NOT NULL, - created_at timestamp without time zone default (CURRENT_TIMESTAMP(0) at time zone 'utc') + created_at timestamp without time zone default (CURRENT_TIMESTAMP(3) at time zone 'utc') ); DO $$ BEGIN diff --git a/spec/02-integration/05-proxy/01-router_spec.lua b/spec/02-integration/05-proxy/01-router_spec.lua index d1dc5ba4fb0..91e89df4d67 100644 --- a/spec/02-integration/05-proxy/01-router_spec.lua +++ b/spec/02-integration/05-proxy/01-router_spec.lua @@ -178,6 +178,60 @@ describe("Router", function() end) end) + describe("URI regexes order of evaluation", function() + setup(function() + helpers.dao:truncate_tables() + + assert(helpers.dao.apis:insert { + name = "api-1", + uris = { "/status/(re)" }, + upstream_url = helpers.mock_upstream_url .. "/status/200", + }) + + ngx.sleep(0.001) + + assert(helpers.dao.apis:insert { + name = "api-2", + uris = { "/status/(r)" }, + upstream_url = helpers.mock_upstream_url .. "/status/200", + }) + + ngx.sleep(0.001) + + assert(helpers.dao.apis:insert { + name = "api-3", + uris = { "/status" }, + upstream_url = helpers.mock_upstream_url .. "/status/200", + }) + + assert(helpers.start_kong({ + nginx_conf = "spec/fixtures/custom_nginx.template", + })) + end) + + teardown(function() + helpers.stop_kong() + end) + + it("depends on created_at field", function() + local res = assert(client:send { + method = "GET", + path = "/status/r", + headers = { ["kong-debug"] = 1 }, + }) + assert.res_status(200, res) + assert.equal("api-2", res.headers["kong-api-name"]) + + res = assert(client:send { + method = "GET", + path = "/status/re", + headers = { ["kong-debug"] = 1 }, + }) + assert.res_status(200, res) + assert.equal("api-1", res.headers["kong-api-name"]) + end) + end) + describe("URI arguments (querystring)", function() setup(function()