Your PHB has gotten the wild notion that the timeserver is going to be so amazingly successful that mutiple machines will be required to satisfy the demand.
Unfortunately, if you run two timeservers (on different machines or even on a single machine with different ports), the login information is not shared between the two servers.
Since credential processing takes so much less time than getting the current time1, the system architect desides on a centralized backend authentication ("auth") server. The timeserver will continue to read and (if necessary) set the user cookie2 but will make calls to the authserver as needed to get the name associated with the cookie.
You will need to create a new main program. Now is a good time to
create a directory in your source tree,
command
,
with subdirectories for each main program
(timeserver
)
and
(authserver
).
You will also need to create a module for the auth client library that will communicate with the auth server.
If you've done Assignment 3 "right", the only change you would need to make to the existing timeserver code will be in your cookie module. If you've inlined the cookie management into the handlers, you will need to do a bit of refactoring.
If you haven't already factored out the code that protects concurrent
access to the dictionary mapping cookie to user name, now is a good
time. I call my protected dictionary
concurrent_map
and import it into client modules with the alias
cmap
3.
You might be able to reuse it in the auth server.
Now is also a good time to factor out the configuration code because
both the timeserver and authserver must share a common flag
--authport
.
The timeserver will require an additional flag,
--authhost
specifying the hostname of the auth server.
Flag processing can be performed by the
init
function4
of the config
module5.
The auth server takes two requests:
/get?cookie=uuid
:
returns status 200 and the name associated with
cookie,
or status 400 if the request is bad. If there is no associated
name, an empty string is returned (with the 200 status).
/set?cooke=cookie&name=name
,
returns 200 if the request was processed, or 400 if the request is
malformed.
Any other requests should return 404.
The timeserver should take a
--authtimeout-ms
flag so it doesn't wait indefinitely for a response that may never
come. Stylistically, it's easier to remember that the timeout is in
milliseconds when the unit is coded into the flag name.
If the authserver fails, the timeserver should treat the user as logged out and clear the cookie.
Note: to get the values of the parameters after the question mark,
you have to call
ParseForm
on the request object to fill in the fields, then call
Get
on the request's
Form
field.
Some additional changes to the timeserver are required to simulate slightly more realistic behavior.
Since getting the current time really doesn't really take so much
computational resources, add two flags
--avg-response-ms
and
--deviation-ms
.
The time request handler will generate a random delay based on average
reponse time (in milliseconds) and the
standard deviation,
also in milliseconds.
Use a
Gaussian
distribution to generate the random number. Clip any negative
delays. Check out Golang's
random number generator package.
Note that the top-level functions of
rand
are
thread-safe6
Add another command-line flag,
--max-inflight
that give the maximum number of in-flight time requests the server can
handle. If unspecified or set to 0, the server will handle as many
as it can, until it falls over.
If the
--max-inflight
flag is given,
the server will keep track of the maximum number of requests it's
currently handling.
Once it reaches
max-inflight
concurrent requests, it will respond to additional requests with
status
Internal Server Error
Note that the current in-flight count will be accessed concurrently by the request handlers, so the count must be protected.
NOTE:
your browser may limit the number of concurrent request that
it
makes.
Test
max-inflight
with a short script that invokes multiple instances of
curl
in the background.
There's a problem with the system: whenever you restart the authserver, all the login information is lost and all users have to log back in. Your tech lead asks you to modify the authserver so that it will periodically dump the login state as a JSON-encoded file.
Add as flags
--dumpfile
and
--checkpoint-interval
.
If
--dumpfile
is specified, when authserver starts up, it will load the dictionary
from
dumpfile
if the dumpfile is present.
If
--checkpoint-interval
is specified,
every
checkpoint-interval
seconds,
the dumpfile, if it exists, will be renamed by adding the extension
.bak
.
Then, concurrent dictionary will be copied to a non-concurrent
dictionary (to minimise the time you're holding the mutex),
JSON-encoded, and written to
dumpfile
.
The dumpfile will be read back, and verified.
If the read-back is successful, the backup file should be deleted.
Hint: you're loading in the dictionary at two different points in the program. How many times should you need to code it?
Consider making use of these standard library packages, json, os, filepath, strings, and any other packages you may find useful.
Submit a tarball of your cleaned workspace, plus README file and any build/run scripts.
Git is no longer required, although it is recommended that you continue using it.