-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(core) upstreams #1735
feat(core) upstreams #1735
Conversation
patch the global tcp.connect function to use the internal dns resolver
…migration process
…rs an empty table if nothing is specified instead of the previous `nil`. So if table length is 0, drop it and revert to defaults.
using postgres it won't start, complaining no options table was provided, yet the debug line shows it does. using cassandra the tcp override debug lines don't show --> cassandra bypasses those overrrides. How??
…cli-tcp-override
Fix/dns2 cli tcp override
…eature/upstreams2
…advanced ring-balancer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First round of reviewing! Left a few "nitpicks" comments that you are free to ignore, but on which we should debate furthermore in the future.
However, a couple of changes are quite important to me, including the log levels mentioned a few times, the format of the log messages, some variable names and the hot code paths good practices :)
PS: we really need to agree on a readable and strictly applied code style.
end, | ||
|
||
POST = function(self, dao_factory, helpers) | ||
local cleanup_factor = 10 -- when to cleanup; invalid-entries > (valid-ones * cleanup_factor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: in your comments, it is very difficult to understand what you mean at the first glance at it because you use semi-colons ;
instead of colons :
.
Semi-colons are meant to separate two independent clauses in a sentence. Colons, on the other side, are used to present an explanation after what could stand as an independent clause. That is, the later should be used when you make a statement, and present the explanation for that statement. Not the former.
I know this can sound like a nitpick but believe me: many times did I found myself re-reading your comments because I did not understand the second clause was the explanation for the first one.
delete[#delete+1] = entry | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style/nitpick: feel free to use more space to make the code more readable. Line jumps, for example, could be inserted at many places in this snippet. A good practice I have recently found myself to be kind of is inserting a blank lines before elseif
and else
statements as well. It makes the whole code greatly more readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At some point, I believe we should formalize the code-style used in this project.
-- either nothing left, or when 10x more outdated than active entries | ||
if (#cleaned == 0 and #delete > 0) or | ||
(#delete >= (math.max(#cleaned,1)*cleanup_factor)) then | ||
ngx.log(ngx.WARN, "Starting cleanup of target table for upstream "..tostring(self.params.upstream_id)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- should be logged at the
INFO
log level. - better to respect good practices and use the variadic arguments form of
ngx.log
: Use,
instead of..
to avoid Lua-land string concatenations. - avoid +80 chars columns
- prefix with the component logging this message:
[admin API]
-- do we need to cleanup? | ||
-- either nothing left, or when 10x more outdated than active entries | ||
if (#cleaned == 0 and #delete > 0) or | ||
(#delete >= (math.max(#cleaned,1)*cleanup_factor)) then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: could use some spacing as well...
-- in case another kong-node does the same cleanup simultaneously | ||
cnt = cnt + 1 | ||
end | ||
ngx.log(ngx.WARN, "Finished cleanup of target table for upstream ".. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto:
- prefix message with component
INFO
log level- avoid Lua-land string concatenations
config.orderlist = t | ||
end | ||
return true | ||
end, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. I'll take your word for this part 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added some comments to clarify
return responses.send_HTTP_INTERNAL_SERVER_ERROR() | ||
return responses.send_HTTP_INTERNAL_SERVER_ERROR("failed to retry the ".. | ||
"dns/balancer resolver for '"..addr.upstream.host.. | ||
"' with; "..tostring(err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use colon instead of semi-colon.
valid, errors, check = validate_entity({ name = "valid.host.name" }, upstreams_schema) | ||
assert.True(valid) | ||
assert.Nil(errors) | ||
assert.Nil(check) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As previously mentioned multiple times before: we have always been using the is_
form of luassert:
assert.is_table
assert.is_string
assert.is_true
assert.is_nil
Capital letters are confusing and carry less meaning than the is_
prefix.
valid, errors, check = validate_entity(data, upstreams_schema) | ||
assert.False(valid) | ||
assert.Nil(errors) | ||
assert.are.equal("invalid orderlist",check.message) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Each of those use cases should be a different test but that's ok.
valid, errors, check = validate_entity(data, targets_schema) | ||
assert.True(valid) | ||
assert.is_nil(errors) | ||
assert.is_nil(check) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is using the is_nil
form as we should. Better stay consistent, another reason to switch to the is_
form altogether.
👍 Question: based on our previous discussion, I think this was the correct decision. Do you have any side effects in mind about this behavior? |
|
||
describe("Ring-balancer #"..kong_config.database, function() | ||
|
||
local config_db |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lint: It appears this variable is unused and is causing the linting to fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Tieske ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was fixed
This more relates to the underlying dns resolving than specifically to this upstream/target implementation. |
I can add multiple identical targets, which should not be allowed, ie the target with host {
"data": [{
"weight": 100,
"id": "ef33c439-0fb7-409f-a7c1-f3e4fb78edf5",
"target": "127.0.0.1:3001",
"created_at": 1482445955029,
"upstream_id": "11920b28-24cb-4fc7-bad7-8124d886fbd6"
}, {
"weight": 100,
"id": "9041af17-7d83-42d5-a04b-3fec76b39512",
"target": "127.0.0.1:3000",
"created_at": 1482445937573,
"upstream_id": "11920b28-24cb-4fc7-bad7-8124d886fbd6"
}, {
"weight": 100,
"id": "59a7f050-f9cb-4cd6-a9f4-399387b05bdc",
"target": "127.0.0.1:3001",
"created_at": 1482445939323,
"upstream_id": "11920b28-24cb-4fc7-bad7-8124d886fbd6"
}],
"total": 3
} |
Also I have an error when properly configuring an upstreams with targets and then making a request to my API:
|
When the DNS cannot find the upstream, the error returned should be
Should be |
Also this happens when no targets have been created:
|
This should be allowed. The targets are not a list of active targets, but a history of changes. So we're not updating an entry, but we're adding a new entry to the history of the upstream. Hence you cannot delete an entry, but only set its
This is not related to the upstream, but to the dns resolver. Currently the dns resolver throws an error if it fails to resolve a name. I'll create a separate issue for this.
This was an issue with the in-memory cache updating only a single worker, instead of all workers. Running tests on the fix right now. |
reloading an upstream, read the upstream and invalidated the balancer. But this code only runs in a single worker. Invalidating the balancer (and recreating it) should be done in very worker.
It now seems to be working better but this error still appears in my logs:
|
fixed. |
* adds loadbalancing on specified targets * adds service registry * implements #157 * adds entities: upstreams and targets * modifies timestamps to millisecond precision (except for the non-related tables when using postgres) * adds collecting health-data on a per-request basis (unused for now)
Summary
Implements the upstreams feature as discussed in #157.
Full changelog
upstream
entity (a loadbalanced upstream, identified by a unique virtual hostname)target
entity (an upstream target, identified by a name/ip + port combination)Issues resolved
Implements #157
Todos
upstream
andtarget
entitiesttl=0
logicfeature/balancer
branch of thedns.lua
moduleUsage
to create an upstream use the following request;
the upstream name can now be used in an api where the hostname is the newly created upstream name;
Targets can now be added to the upstreams;
The target can be an IP address or name. The name will be resolved and all entries will be added with the same weight. So if in the example above
service.v1.host1
resolves to an A record containing ip addresses1.2.3.4
and5.6.7.8
, then they will both be added with weight10
. When a record expires (ttl) it will be refreshed upon the first access after expiring.When the name resolves to an SRV record, then both the specified port and weight will be ignored. The information from the SRV record will be used instead. For SRV records, only the primary servers will be used (the entries with the lowest priority settings).
A dns record with a ttl setting of 0 will be added as a single target (even if it resolves to multiple ip addresses) and will be resolved upon each request.
To remove a
target
, do another post request, but setweight=0