Skip to content

Commit f8495f1

Browse files
committed
Fix race condition on seeding database on startup. Improve startup logs.
If nginx got started before MongoDB/Mora was fully responsive, there was a possibility seeding the initial database data would fail. This improves the seeding process by waiting for MongoDB to be fully up before proceeding with the seeding. Seeding will be re-attempted until it succeeds. Some related improvements were made to the similar Elasticsearch setup process. We were already waiting there to ensure Elasticsearch was up before running setup, but if Elasticsearch timed out, then setup wasn't re-attempted for another 1 hour. This improves things so that setup is reattempted on startup until it succeeds. This also quiets the startup logs by only logging events when MongoDB or Elasticsearch aren't ready after the 60 second timeout period (rather than on each connection attempt).
1 parent 4a26913 commit f8495f1

File tree

3 files changed

+64
-15
lines changed

3 files changed

+64
-15
lines changed

src/api-umbrella/proxy/jobs/elasticsearch_setup.lua

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ local function wait_for_elasticsearch()
1717
repeat
1818
local res, err = httpc:request_uri(elasticsearch_host .. "/_cluster/health")
1919
if err then
20-
ngx.log(ngx.ERR, "failed to fetch cluster health from elasticsearch: ", err)
20+
ngx.log(ngx.NOTICE, "failed to fetch cluster health from elasticsearch (this is expected if elasticsearch is starting up at the same time): ", err)
2121
elseif res.body then
2222
local elasticsearch_health = cjson.decode(res.body)
2323
if elasticsearch_health["status"] == "yellow" or elasticsearch_health["status"] == "green" then
@@ -30,6 +30,12 @@ local function wait_for_elasticsearch()
3030
wait_time = wait_time + sleep_time
3131
end
3232
until elasticsearch_alive or wait_time > max_time
33+
34+
if elasticsearch_alive then
35+
return true, nil
36+
else
37+
return false, "elasticsearch was not ready within " .. max_time .."s"
38+
end
3339
end
3440

3541
local function create_templates()
@@ -101,14 +107,20 @@ local function create_aliases()
101107
end
102108
end
103109

104-
local function do_check()
105-
wait_for_elasticsearch()
106-
create_templates()
107-
create_aliases()
110+
local function setup()
111+
local _, err = wait_for_elasticsearch()
112+
if not err then
113+
create_templates()
114+
create_aliases()
115+
else
116+
ngx.log(ngx.ERR, "timed out waiting for eleasticsearch before setup, rerunning...")
117+
ngx.sleep(5)
118+
setup()
119+
end
108120
end
109121

110122
function _M.spawn()
111-
interval_lock.repeat_with_mutex('elasticsearch_index_setup', delay, do_check)
123+
interval_lock.repeat_with_mutex('elasticsearch_index_setup', delay, setup)
112124
end
113125

114126
return _M

src/api-umbrella/proxy/startup/seed_database.lua

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,37 @@
11
local deep_merge_overwrite_arrays = require "api-umbrella.utils.deep_merge_overwrite_arrays"
2-
local lock = require "resty.lock"
2+
local interval_lock = require "api-umbrella.utils.interval_lock"
33
local mongo = require "api-umbrella.utils.mongo"
44
local random_token = require "api-umbrella.utils.random_token"
55
local uuid = require "resty.uuid"
66

77
local nowMongoDate = { ["$date"] = { ["$numberLong"] = tostring(os.time() * 1000) } }
88

9+
local function wait_for_mongodb()
10+
local mongodb_alive = false
11+
local wait_time = 0
12+
local sleep_time = 0.5
13+
local max_time = 14
14+
repeat
15+
local _, err = mongo.collections()
16+
if err then
17+
ngx.log(ngx.NOTICE, "failed to establish connection to mongodb (this is expected if mongodb is starting up at the same time): ", err)
18+
else
19+
mongodb_alive = true
20+
end
21+
22+
if not mongodb_alive then
23+
ngx.sleep(sleep_time)
24+
wait_time = wait_time + sleep_time
25+
end
26+
until mongodb_alive or wait_time > max_time
27+
28+
if mongodb_alive then
29+
return true, nil
30+
else
31+
return false, "elasticsearch was not ready within " .. max_time .."s"
32+
end
33+
end
34+
935
local function seed_api_keys()
1036
local keys = {
1137
@@ -208,19 +234,24 @@ local function seed_admin_permissions()
208234
end
209235

210236
local function seed()
211-
local seed_lock = lock:new("locks", { ["timeout"] = 0 })
212-
local _, lock_err = seed_lock:lock("seed_database")
213-
if lock_err then
214-
return
237+
local _, err = wait_for_mongodb()
238+
if not err then
239+
seed_api_keys()
240+
seed_initial_superusers()
241+
seed_admin_permissions()
242+
else
243+
ngx.log(ngx.ERR, "timed out waiting for mongodb before seeding, rerunning...")
244+
ngx.sleep(5)
245+
seed()
215246
end
247+
end
216248

217-
seed_api_keys()
218-
seed_initial_superusers()
219-
seed_admin_permissions()
249+
local function seed_once()
250+
interval_lock.mutex_exec("seed_database", seed)
220251
end
221252

222253
return function()
223-
local ok, err = ngx.timer.at(0, seed)
254+
local ok, err = ngx.timer.at(0, seed_once)
224255
if not ok then
225256
ngx.log(ngx.ERR, "failed to create timer: ", err)
226257
return

src/api-umbrella/utils/mongo.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ function _M.find(collection, query_options)
120120
return results, err
121121
end
122122

123+
function _M.collections()
124+
local collection = ""
125+
local query_options = {}
126+
return _M.find(collection, query_options)
127+
end
128+
123129
function _M.first(collection, query_options)
124130
if not query_options then
125131
query_options = {}

0 commit comments

Comments
 (0)