How to ensure atomicity of RMI operations?

I want to update a file using RMI. How can I ensure the status of operations.

The client is connected to the server and calls the method. Immediately after this, the connection between the client and the server is interrupted and the client receives a RemoteException , but the called method continues to work, and then changes the file, and then returns and receives an exception (suppose that it will realize the connection that was lost when writing the response to the socket).

+4
source share
4 answers

This is quite difficult to do in practice. You will need to implement a two-phase protocol where the client can get an indicator that the server can guarantee a commit, and then commit.

There is a predefined protocol called XA to manage this process, and this is supported by the Java Transaction API . Essentially, you can create an XA-compliant resource manager for your file.

This is not a trivial undertaking, but it is not beyond the scope of your ability, if you do not mind, including the application server or transaction manager in your infrastructure. The advantage of this approach is that you can connect to a mature, streamlined transaction management infrastructure.

Several alternatives:

  • Implement your own two-phase commit protocol or

  • Structure the update code of the file, which is idempotent (several calls to make the same update, just do this update). In this case, you can retry updating until your client receives a success indicator from the server.

    Please note that you will also have to come up with a protocol to block and / or manage conflicting updates on the same record if you cannot guarantee a consistent record from the client. Depending on your application, this may be an optimistic concurrency protocol, but you still have to block the range that you are working on while working on it to make the atomic record.

+5
source

You are dealing with distributed processing. This means that you cannot guarantee that any message (method call, etc.) will arrive at the other end; a connection can always be dropped arbitrarily. Because of this, you need a mitigation strategy that is independent of any particular message.

One of these strategies is to use a reliable messaging system, which essentially sets up databases for storing message queues, which are then reliably transmitted in order (by sending a message marked with a sequence identifier several times until confirmation is received), but this is a lot overhead costs, and they should probably be kept for important things (such as financial transactions).

Another strategy is to use a distributed transaction manager, but they have pretty big problems (unless you are implementing a distributed consensus system and are not complicated and still have potential failure modes).

I think the easiest way is to reorganize what is considered final. Ask the client to contact the server to collect a temporary description of the operation that should be recorded on the server (including a unique identifier, such as a UUID), after which the client can send a short message that starts the commit; At this point, the server needs to register that it has started work on processing this UUID. If the answer cannot be sent, it is still after fixing. If any message is lost, it does not matter much: either before the start of the commit (in this case it did not happen, but can be repeated) or after that in this case there will be a permanent record in which it was taken, and so easily inform what happened ("I'm still working on it," "I succeeded," "I failed."). The only state the client needs to remember is the UUID, and this can be highlighted outside of the transaction (a wonderful UUID property, which, admittedly, is only probabilistically true, but the likelihood of the problem is really negligible).

0
source

You can pass a sequential counter argument from client to server with each operation. Depending on your usage scenario, your server will need to remember one or more of the most recently sent counters. if you see that this has already happened, you can return success (or something else). If the values ​​are to be unique across multiple clients, you can use something like a UUID (mentioned by another answer). Much depends on what the return values ​​are and what the sequence of calls looks like.

As an example, I implemented this idea in the rmiio project to deal with this very problem. in the rmiio library you only need to deal with the possibility of repeating the "current" operation, so you only need to track the last value of the counter.

0
source

If you decide to flip your own pseudo-transaction system, here are the main ones:

On the server side:

  • Make a callback from the server to the client to indicate that you are ready to write the file. If this gives an error, cancel the procedure.
  • Only if the callback succeeded, actually write the file.

On the client:

  • When you get a RemoteException, check if the callback was completed or not.
  • If the callback was executed, you can be β€œsure” that the rest of the procedure will be executed, and the RemoteException has occurred for another reason.
0
source

Source: https://habr.com/ru/post/1389275/


All Articles