2.1 Updates:

Joke Server State Concepts

Three simple state maintenance schemes:

IMPLEMENTATION ONE:

  1. When the client starts running, it initializes by producing a UUID (there are Java libraires for this, but you can use a Good enough UUID ), which is stored in a local variable.

  2. This UUID is bound to the conversation state for this client, and can always be used to retrieve current state of the conversation from some permanent store on the server.

  3. A request protocol is used such that every time a message is passed to the server, the UUID is sent with it.

  4. The server maintains the entire state for the client/server interaction and stores it in memory.

  5. Whenver the client connects it ALWAYS sends the UUID, and the server uses the UUID to retrieve the state. If the server has no state for the UUID, it initializes one to the start state.

  6. That's it: generate a UUID yourself on the client, store it in a variable, send it the server each time you connect.

IMPLEMENTATION TWO: An alternative, but equally simple, implementation, is to generate the ID at the server the first time the client connects and send it back to the client. Store it on the client and proceed as above. In this case a simple counter will do (an ID). A UUID is not needed. Each time there is a new client, the counter is incremented. Thus the first client would be identified as "1," the second as "2," the third as "3," and so forth. This will not work with multiple servers, because the IDs would no longer be unique.

IMPLEMENTATION THREE: Another alternative, but equally simple implementation is to skip the UUID altogether, and send the entire state back to client, saving nothing at the server. In this case you would be sending back two sets of random orderings, one each for the jokes and the proverbs, and a postion in each ordering representing which jokes/proverbs have already been sent. It is not necessary to send the actual jokes or proverbs as part of the state. (In fact, see below where the entire state is kept in eight bits, plus the length of the user's name.) When the client re-connects, it sends the whole state. The server looks to see which joke or proverb has NOT been sent, retrieves the data for that joke or proverb, updates the state, and sends the joke or proverb and the updated state back to the user. Then the server breaks the connection, and discards ALL information about the conversation.

Storing the entire state in 8 bits, plus the name.

Okay—let's think this through. With four items there are 24 (4 factorial) orderings. (That is, four ways to pick the first joke, times three ways to pick the second joke, times two the third, times one the fourth). 2 to the 5th power is 32, so five bits is enough to allow us to identify the 24 joke orderings, and five bits the 24 proverb orderings. (That is, we have labeled each ordering with a number, so we have to keep a table associating each number with an ordering of the jokes, and the proverbs—but we do not have to send the table, or the table row with the ordering in it as part of the state, we can just send the number.) Then we need three bits to count from zero to four to know how many jokes in the set have been sent, and three bits for the proverbs. That makes 16 bits to store the entire state.

Of course if we were clever we could get by with eight bits, where for each set of four a "1" meant that joke/proverb had been sent, and a "0" meant it had not

Now, it will take some processing to match the jokes, and the orderings, with the bits, and also to re-generate a random order each time we have completed a cycle of jokes, but in any case the entire state takes significantly fewer bits than does a reliable UUID.

The important concept to remember is the state is not the data. If we replace our four jokes with four database entries with 3 terabytes of data each, our state still only takes eight bits.

Remember that the name of the user entered must also be stored as part of the state.

Other considerations

For this kind of client-server system, where do you want the processing to take place? Where do you want to store the data? Each of these will determine the best design for saving the state.

If the server is maintaining the state, and a client does not reconnect, what does it mean? Is the server obligated to save the state on behalf of the client for a theoretical eternity? Do you need a graceful way to tell the server that a client is finished processing and the state can be released? If so, how is a client failing different from a client that is simply in a long dormant period? What happens when you update the server to a new version? How do you guarantee that all the clients will be updated as well? What happens when a client is trying to connect and does not get a response? Has the server failed, or is the server possibly just very busy?