Skip to content

Commit 938929b

Browse files
authored
Merge pull request #1500 from tkan145/THREESCALE-9301-dns-cache-miss
[THREESCALE-9301] Fix dns cache miss
2 parents b4c1fa1 + 30711af commit 938929b

8 files changed

Lines changed: 134 additions & 71 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2020
- Fixed Conditional policy evaluating incorrectly: second policy in policy chain that implement export() always triggers [PR #1485](https://github.com/3scale/APIcast/pull/1485) [THREESCALE-9320](https://issues.redhat.com/browse/THREESCALE-9320)
2121
- Fix APIcast using stale configuration for deleted products [PR #1488](https://github.com/3scale/APIcast/pull/1488) [THREESCALE-10130](https://issues.redhat.com/browse/THREESCALE-10130)
2222
- Fixed Mutual TLS between APIcast and the Backend API fails when using a Forward Proxy [PR #1499](https://github.com/3scale/APIcast/pull/1499) [THREESCALE-5105](https://issues.redhat.com/browse/THREESCALE-5105)
23+
- Fixed dns cache miss [PR #1500](https://github.com/3scale/APIcast/pull/1500) [THEESCALE-9301](https://issues.redhat.com/browse/THREESCALE-9301)
2324

2425
### Added
2526

gateway/src/resty/resolver.lua

Lines changed: 62 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,18 @@ local dns_client = require 'resty.resolver.dns_client'
1818
local resty_env = require 'resty.env'
1919
local upstream = require 'ngx.upstream'
2020
local re = require('ngx.re')
21-
local semaphore = require "ngx.semaphore"
21+
local semaphore = require "ngx.semaphore".new(1)
2222
local synchronization = require('resty.synchronization').new(1)
23-
24-
local init = semaphore.new(1)
23+
local table_new = require("table.new")
2524

2625
local default_resolver_port = 53
2726

2827
local _M = {
2928
_VERSION = '0.1',
3029
}
3130

31+
local TYPE_A = 1
32+
3233
local mt = { __index = _M }
3334

3435
local function read_resolv_conf(path)
@@ -171,14 +172,14 @@ function _M.init_nameservers(path)
171172
end
172173

173174
function _M.nameservers()
174-
local ok, _ = init:wait(0)
175+
local ok, _ = semaphore:wait(0)
175176

176177
if ok and #(_M._nameservers) == 0 then
177178
_M.init()
178179
end
179180

180181
if ok then
181-
init:post()
182+
semaphore:post()
182183
end
183184

184185
return _M._nameservers
@@ -287,65 +288,68 @@ local function valid_answers(answers)
287288
return answers and not answers.errcode and #answers > 0 and (not answers.addresses or #answers.addresses > 0)
288289
end
289290

290-
local function search_dns(self, qname, stale)
291+
local function resolve_upstream(qname)
292+
local peers, err = upstream.get_primary_peers(qname)
291293

292-
local search = self.search
293-
local dns = self.dns
294-
local options = self.options
295-
local cache = self.cache
294+
if not peers then
295+
return nil, err
296+
end
296297

297-
local function get_answer(query)
298-
local answers, err
299-
answers, err = cache:get(query, stale)
300-
if valid_answers(answers) then
301-
return answers, err
302-
end
298+
for i=1, #peers do
299+
local m = re.split(peers[i].name, ':', 'oj')
303300

304-
answers, err = dns:query(query, options)
305-
if valid_answers(answers) then
306-
cache:save(answers)
307-
return answers, err
308-
end
309-
return nil, err
301+
peers[i] = new_answer(m[1], m[2])
310302
end
311303

304+
return peers
305+
end
306+
307+
-- construct search list from resolv options: search
308+
-- @param search table of search domain
309+
-- @param qname the name to query for
310+
-- @return table with search names
311+
local function search_list(search, qname)
312+
-- FQDN
312313
if sub(qname, -1) == "." then
313314
local query = sub(qname, 1 ,-2)
314-
ngx.log(ngx.DEBUG, 'resolver query: ', qname, ' query: ', query)
315-
return get_answer(query)
315+
return {query}
316316
end
317317

318-
local answer, err
318+
local names = table_new(#search +1, 0)
319319
for i=1, #search do
320-
local query = qname .. '.' .. search[i]
321-
ngx.log(ngx.DEBUG, 'resolver query: ', qname, ' search: ', search[i], ' query: ', query)
322-
answer, err = get_answer(query)
323-
if answer then
324-
return answer, err
325-
end
320+
names[i] = qname .. "." .. search[i]
326321
end
327322

328-
return nil, err
323+
return names
329324
end
330325

331-
local function resolve_upstream(qname)
332-
local peers, err = upstream.get_primary_peers(qname)
326+
local function search_dns(self, qname)
333327

334-
if not peers then
335-
return nil, err
336-
end
328+
local search = self.search
329+
local dns = self.dns
330+
local options = self.options
331+
local queries = search_list(search, qname)
332+
local answers, err
337333

338-
for i=1, #peers do
339-
local m = re.split(peers[i].name, ':', 'oj')
334+
-- Nothing found, append search domain and query DNS server
335+
-- Return the first valid answer
336+
for _, query in ipairs(queries) do
337+
ngx.log(ngx.DEBUG, 'resolver query: ', qname, ' query: ', query)
340338

341-
peers[i] = new_answer(m[1], m[2])
339+
answers, err = dns:query(query, options)
340+
if valid_answers(answers) then
341+
return answers, err
342+
end
342343
end
343344

344-
return peers
345+
return nil, err
345346
end
346347

348+
347349
function _M.lookup(self, qname, stale)
348350
local cache = self.cache
351+
local options = self.options
352+
local qtype = options.qtype or TYPE_A
349353

350354
ngx.log(ngx.DEBUG, 'resolver query: ', qname)
351355

@@ -355,20 +359,28 @@ function _M.lookup(self, qname, stale)
355359
ngx.log(ngx.DEBUG, 'host is ip address: ', qname)
356360
answers = { new_answer(qname) }
357361
else
358-
if is_fqdn(qname) then
359-
answers, err = cache:get(qname, stale)
360-
else
361-
answers, err = resolve_upstream(qname)
362+
local key = qname .. ":" .. qtype
363+
364+
-- Check cache first
365+
answers, err = cache:get(key, stale)
366+
if valid_answers(answers) then
367+
return answers, nil
362368
end
363369

364-
if not valid_answers(answers) then
365-
answers, err = search_dns(self, qname, stale)
370+
if not is_fqdn(qname) then
371+
answers, err = resolve_upstream(qname)
372+
373+
if valid_answers(answers) then
374+
return answers, nil
375+
end
366376
end
367377

378+
answers, err = search_dns(self, qname)
379+
if answers then
380+
cache:save(qname, qtype, answers)
381+
end
368382
end
369383

370-
ngx.log(ngx.DEBUG, 'resolver query: ', qname, ' finished with ', #(answers or empty), ' answers')
371-
372384
return answers, err
373385
end
374386

@@ -390,6 +402,7 @@ function _M.get_servers(self, qname, opts)
390402
local ok = sema:wait(0)
391403

392404
local answers, err = self:lookup(qname, not ok)
405+
ngx.log(ngx.DEBUG, 'resolver query: ', qname, ' finished with ', #(answers or empty), ' answers')
393406

394407
if ok then
395408
-- cleanup the key so we don't have unbounded growth of this table

gateway/src/resty/resolver/cache.lua

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ local _M = {
1212
_VERSION = '0.1'
1313
}
1414

15+
1516
local mt = { __index = _M }
1617

1718
local shared_lrucache = resty_lrucache.new(1000)
@@ -35,17 +36,18 @@ local function compact_answers(servers)
3536

3637
if server then
3738
local name = server.name or server.address
39+
local type = server.type
3840

3941
local packed = hash[name]
40-
4142
if packed then
4243
insert(packed, server)
4344
packed.ttl = min(packed.ttl, server.ttl)
4445
else
4546
packed = {
4647
server,
4748
name = name,
48-
ttl = server.ttl
49+
ttl = server.ttl,
50+
type = type,
4951
}
5052

5153
insert(compact, packed)
@@ -57,7 +59,7 @@ local function compact_answers(servers)
5759
return compact
5860
end
5961

60-
function _M.store(self, answer, force_ttl)
62+
function _M.store(self, qname, qtype, answer, force_ttl)
6163
local cache = self.cache
6264

6365
if not cache then
@@ -71,23 +73,36 @@ function _M.store(self, answer, force_ttl)
7173
return nil, 'invalid answer'
7274
end
7375

74-
ngx.log(ngx.DEBUG, 'resolver cache write ', name, ' with TLL ', answer.ttl)
76+
local type = answer.type
77+
78+
if not type then
79+
ngx.log(ngx.WARN, 'resolver cache write refused invalid answer type ', inspect(answer))
80+
return nil, 'invalid answer'
81+
end
82+
83+
if type == qtype then
84+
name = qname
85+
end
86+
7587

7688
local ttl = force_ttl or answer.ttl
7789

7890
if ttl == -1 then
7991
ttl = nil
8092
end
8193

82-
return cache:set(name, answer, ttl)
94+
local key = name .. ":" .. qtype
95+
ngx.log(ngx.DEBUG, 'resolver cache write ', key, ' with TLL ', ttl)
96+
97+
return cache:set(key, answer, ttl)
8398
end
8499

85100

86-
function _M.save(self, answers)
101+
function _M.save(self, qname, qtype, answers)
87102
local ans = compact_answers(answers or {})
88103

89104
for _, answer in pairs(ans) do
90-
local _, err = self:store(answer)
105+
local _, err = self:store(qname, qtype, answer)
91106

92107
if err then
93108
return nil, err

script/resolver

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
#!/usr/bin/env sh
22

3-
exec resty -I apicast/src script/resolver.lua "$@"
3+
exec resty -I gateway/src script/resolver.lua "$@"

spec/resty/resolver/cache_spec.lua

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ describe('resty.resolver.cache', function()
3939
it('returns compacted answers', function()
4040
local keys = {}
4141

42-
for _,v in ipairs(c:save(answers)) do
42+
for _,v in ipairs(c:save('www.example.com', 1, answers)) do
4343
table.insert(keys, v.name)
4444
end
4545

@@ -51,7 +51,7 @@ describe('resty.resolver.cache', function()
5151
it('stores the result', function()
5252
c.store = spy.new(c.store)
5353

54-
c:save(answers)
54+
c:save('eld.example.com', 1, answers)
5555

5656
assert.spy(c.store).was.called(3) -- TODO: proper called_with(args)
5757
end)
@@ -63,40 +63,51 @@ describe('resty.resolver.cache', function()
6363

6464
it('writes to the cache', function()
6565
local record = { 'someting' }
66-
local answer = { record, ttl = 60, name = 'foo.example.com' }
66+
local answer = { record, ttl = 60, name = 'foo.example.com', type = 1 }
6767
c.cache.set = spy.new(function(_, key, value, ttl)
68-
assert.same('foo.example.com', key)
68+
assert.same('foo.example.com:1', key)
6969
assert.same(answer, value)
7070
assert.same(60, ttl)
7171
end)
7272

73-
c:store(answer)
73+
c:store('foo.example.com', 1, answer)
7474

7575
assert.spy(c.cache.set).was.called(1)
7676
end)
7777

7878
it('works with -1 ttl', function()
79-
local answer = { { 'something' }, ttl = -1, name = 'foo.example.com' }
79+
local answer = { { 'something' }, ttl = -1, name = 'foo.example.com', type = 1 }
8080

8181
c.cache.set = spy.new(function(_, key, value, ttl)
82-
assert.same('foo.example.com', key)
82+
assert.same('foo.example.com:1', key)
8383
assert.same(answer, value)
8484
assert.same(nil, ttl)
8585
end)
8686

87-
c:store(answer)
87+
c:store('foo.example.com', 1, answer)
8888

8989
assert.spy(c.cache.set).was.called(1)
9090
end)
91+
92+
it('return error when name is missing', function()
93+
local answer = { { 'something' }, ttl = -1 }
94+
c.cache.set = spy.new(function(_, key, value, ttl)
95+
end)
96+
97+
local _, err = c:store('something', 1, answer)
98+
99+
assert.same(err, "invalid answer")
100+
assert.spy(c.cache.set).was_not_called()
101+
end)
91102
end)
92103

93104
describe('.get', function()
94105
local c = resolver_cache.new()
95106

96107
it('returns answers', function()
97-
c:save(answers)
108+
c:save('www.example.com', 1, answers)
98109

99-
local ans = c:get('www.example.com')
110+
local ans = c:get('www.example.com:1')
100111

101112
assert.same({ "54.221.208.116", "54.221.221.16" }, ans.addresses)
102113
end)

spec/resty/resolver/http_spec.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ describe('resty.resolver.http', function()
1414
it('resolves localhost', function()
1515
local client = _M.new()
1616
client:set_timeout(1000)
17-
client.resolver.cache:save({ { address = '127.0.0.1', name = 'unknown.', ttl = 1800 } })
17+
client.resolver.cache:save('unknown', 1, { { address = '127.0.0.1', name = 'unknown.', ttl = 1800 , type=1} })
1818
assert(client:connect({scheme="http", host='unknown', port=1984}))
1919
assert.equal('unknown', client.host)
2020
assert.equal(1984, client.port)

spec/resty/resolver/socket_spec.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ describe('resty.resolver.socket', function()
1616
sock:settimeout(1000)
1717
local wrapper = _M.new(sock)
1818

19-
wrapper.resolver.cache:save({ { address = '127.0.0.1', name = 'unknown.', ttl = 1800 } })
19+
wrapper.resolver.cache:save('unknown', 1, { { address = '127.0.0.1', name = 'unknown.', ttl = 1800, type = 1} })
2020
assert(wrapper:connect('unknown', 1984))
2121
assert.equal('unknown', wrapper.host)
2222
assert.equal(1984, wrapper.port)

0 commit comments

Comments
 (0)