-
Notifications
You must be signed in to change notification settings - Fork 17
includes server header in output generated from waiter #542
Conversation
(defn generate-secret-word | ||
[src-id dest-id processed-passwords] | ||
(let [password (second (first processed-passwords)) | ||
secret-word (digest/md5 (str src-id ":" dest-id ":" password))] | ||
(log/debug "generate-secret-word" [src-id dest-id] "->" secret-word) | ||
secret-word)) | ||
|
||
(let [server-name-atom (atom "waiter")] |
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.
Why wrap this at all? Why not just have the bare atom? Less code, and I can do a 'find uses' of the atom and see all gets and sets, globally. Now, I'd need to search for the atom, then for these function uses.
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.
The intent is to restrict the global access to this atom. Only the two functions inside the let
block can access this atom directly.
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.
Almost everything in clojure is global, by design. What we lose by hiding it behind these accessors is
#1: We have 9 lines of code; 10% of the lines in this patch and 33% of the new code added in this patch.
#2: We can no longer track accesses to the server name atom so easily, with, e.g., the IDE. We have to now trace indirectly via the finding the references to these two functions (versus searching for the one atom.)
The atom is still mutable, except instead of doing (reset! ...) its now (reset-server-name-atom!) and (get-current-server-name ...) instead of (deref).
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.
-
Almost everything in Clojure is global, but you would need to go through many hoops to access this atom in the
let
block. -
The pattern of restricting access to the atom is not new to the code, e.g.
waiter/waiter/src/waiter/util/utils.clj
Line 401 in f6bf706
(let [messages (atom {})] -
By restricting access to the atom behind a function (
current-server-name
) we have hidden away the implementation of how the server name is retrieved. This gives us the flexibility to change the implementation without impacting the callers of this method (e.g. what if we later chose to retrieve the server name from an environment variable). -
By introducing
reset-server-name-atom!
it is now easy to find any code paths that mutate the atom. If we had the raw atom, we would have to find its usages and then introspect the code for uses ofswap!
orreset!
on it.
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.
Good find; I'd make the same claim there; the code could be more direct. (That said I'd advocate renaming that atom as i18l-messages. The current name is far too generic. :) )
Responding to the points:
- Is the concern about flexibility on how the server name is retrieved a concrete or hypothetical concern? Could you elaborate?
- With a handful of accesses, the IDE search makes it easy to identify the 3 places accessing the variable, so finding which of them sets it is pretty easy (the one without the @)
My reasoning above assumes we continue to use an IDE that can find all the references to the atom in 3 seconds via control-click, that we have <5 references to it, and being so simple and easily found, changing the code is straightforward.
Right now, I see a way to simply the patch by removing a third of the added lines of code, but if you feel strongly that the value described above outweighs the added complexity, I'll agree to disagree and accept the patch. :)
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.
The flexibility is, currently, a hypothetical concern. But I would not be surprised if we needed to change the implementation in the future. E.g. in the case of messages chose to dynamically reload the messages from a properties file or some other store.
"Handful of accesses" is the current state, but may not be as the code evolves.
I feel like this is a scenario ideal for the use of mount
. We want to initialize some state (we generate from the settings) and then let it be freely read without worry of it being mutated. When we can control the mutation to only the initialization, there is no harm in having everyone else access it directly to read. I am mimicking that by restricting access to the raw atom and instead requiring invocation of a helper function to read the state.
I'd very much appreciate if you accept the patch, we can have a more general discussion on how these should be handled and perform a more complete refactoring on the codebase for such uses of atom once we reach a consensus.
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.
I'm fine with approving it, but apparently I don't have commit permissions to waiter to commit it.
waiter/test/waiter/core_test.clj
Outdated
@@ -434,7 +434,9 @@ | |||
(let [request {:request-method :delete, :uri (str "/apps/" service-id), :authorization/user user} | |||
{:keys [body headers status]} (async/<!! ((ring-handler-factory waiter-request?-fn handlers) request))] | |||
(is (= 200 status)) | |||
(is (= {"content-type" "application/json"} headers)) | |||
(is (= {"content-type" "application/json" |
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.
These look like standard headers, repeated about 40 times.
Why not:
(def expected-error-response-headers {....})
Then reference that dictionary throughout?
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.
Introduced a new variable to hold the expected headers and left the assertions in the tests.
waiter/test/waiter/scaling_test.clj
Outdated
@@ -166,7 +166,10 @@ | |||
{:basic-authentication {:src-router-id src-router-id} :route-params {:service-id test-service-id}})) | |||
{:keys [body headers status]} (async/<!! response-chan)] | |||
(is (= 200 status)) | |||
(is (= {"content-type" "application/json", "x-cid" correlation-id} headers)) | |||
(is (= {"content-type" "application/json" |
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.
Could factor this out in a similar way.
(defn verify-response-headers
[headers expected-corrolation-id]
(is ....))
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.
Introduced a new variable to hold the expected headers and left the assertions in the tests.
@@ -118,7 +118,9 @@ | |||
(testing "should convert empty map" | |||
(let [{:keys [body headers status]} (clj->json-response {})] | |||
(is (= 200 status)) | |||
(is (= {"content-type" "application/json"} headers)) | |||
(is (= {"content-type" "application/json" |
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 here.
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.
Introduced a new variable to hold the expected headers and left the assertions in the tests.
420af5c
to
f6bf706
Compare
@scrosby ready for another round :) |
Changes proposed in this PR
Why are we making these changes?
Adding the
Server
header allows clients to differentiate between responses originating in Waiter and the backend.Before:
After (note the extra
server
header):