> use the index of the previous request as the read index, instead of the
commit index

Yes. This is exactly what we semantically want to do. Here are two
questions we need to answer:

1. Who drive the new read index process?
2. How to fulfill "the previous request's index" as read index?

They can be the same problem.

If the client drives the process, then the only way it knows "the previous
request's index" is waiting for the previous request return. This is no
better than blocking IO or thenApply.

If the server drives the process, then "the previous request" means the
previous request of "this connection". I don't find a connection
abstraction in the codebase and this is what I encounter issues on the
first place: query/applyTransaction accept requests from all the clients
synchronously and not even in order to when they arrive the server.

Best,
tison.


tison <[email protected]> 于2023年8月29日周二 07:00写道:

> Thank Wiliam & Tzs Wo!
>
> Yeah I just realized that it's somehow a "client-side" or
> "connection-aspect" issue.
>
> The issue of thenApply is the same as blocking IO that we cannot pipeline
> requests but only send the next read after the pioneer write return from
> the server.
>
> We can resolve this issue if we use Ratis as IoTDB that wraps the
> RaftServer with a facade and handles server-side connection customizedly.
> That is, the client pipelines requests, but the server sequences the
> requests and `submitClientRequest` with thenApply chain. This is like how
> ZooKeeper's CommitProcessor chains its read/write requests[1].
>
> So here we may have another perspective on the question: what are the
> expected methods of application based on Ratis?
>
> If we want users to write applications with RaftClient in App's
> client-side and only customized StateMachine on App's server side, even use
> RaftServer as the server facade, then the current query/applyTransaction,
> and even the fixed one-shot RPC[2] can be a limitation.
>
> If we encourage users to wrap RaftServer with their own server facade and
> use RaftClient in the App's server-side on demand like IoTDB does, then
> maybe the experience of submitting requests on the server side can be
> improved; or we provide some examples to do so. (Generally, it should be
> fine to always use RaftClient in the App's server-side, while it can cause
> one extra codec, but we already have to send the request to other peers on
> writes.)
>
> Best,
> tison.
>
> [1]
> https://github.com/apache/zookeeper/blob/bc0e61854ca362261d899295422ac14dd5d82e59/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/CommitProcessor.java#L251-L259
> [2] https://lists.apache.org/thread/rg65qoph54hlpdhmoc3p80sd0z6wzwjm
>
> Tsz Wo Sze <[email protected]> 于2023年8月29日周二 02:36写道:
>
>> HI tison, William,
>>
>> Thanks for pointing out the problem!
>>
>> For the long term, we should support Read-After-Write Consistency.  It
>> seems easy to modify the read-index algorithm to support it: use the index
>> of the previous request as the read index, instead of the commit index.
>>
>> As a work around, we may use the async API with thenApply:
>> - Client 1: async put k1 as v1 and get future f1
>> - Client 1: f1.thenApply(reply -> if (reply.isSucces()) {async get k1}).
>>
>> Tsz-Wo
>>
>>
>> On Mon, Aug 28, 2023 at 8:28 AM William Song <[email protected]> wrote:
>>
>>> Hi tison,
>>>
>>> The readIndex provides only the linearizable consistency guarantee. A
>>> linearizable read returned by RaftServer at least reflects any changes
>>> already committed and replied to the client. If you require a more strict
>>> consistency guarantee, like read reflecting the latest write, you may need
>>> to add extra synchronization logic. I think blocking IO or dependencies on
>>> completionStage will both work.
>>>
>>> Regards,
>>> William
>>>
>>>
>>> 2023年8月27日 12:20,tison <[email protected]> 写道:
>>>
>>> Hi,
>>>
>>> I have asked a related question before[1] about syncing better query
>>> and applytransaction.
>>>
>>> Now, when I dive deeper into the Raft-based system, I notice a new case
>>> for consistency.
>>>
>>> Take the following executions as an example (supposed we implement a Map
>>> statemachine):
>>>
>>> Client 1 put k1 as v1
>>> Client 1 get k1
>>>
>>> Generally, we expect the get requests gets v1. But if these two requests
>>> are issued in async, it's not always true.
>>>
>>> Even the client TCP connection can guarantee that "get k1" goes after
>>> "put k1", and we do read index for "get k1", the read index returned can be
>>> lower then "put k1" if it's not yet committed. Then the "get k1" request
>>> gets an empty value.
>>>
>>> I'm not sure what is the best practices to implement read my write for a
>>> Ratis-based system.
>>>
>>> 1. If we're using blocking IO, it won't be an issue cause "get k1" must
>>> be after "put k1" committed and even applied.
>>> 2. If we build an extra facade over Ratis like IoTDB does, we can check
>>> the read conditions as I did at [2].
>>> 3. But what if we only customize the statemachine and use the Ratis
>>> client? I don't know which happens before relations I can depend on for
>>> concurrent query and applytransaction.
>>>
>>> Also, in [2] for raft-rs there is one more subtle issue that their
>>> ReadIndex request can get lost or silently ignored, which causes the caller
>>> to retry that can even get a much later read index. I'm curious if Ratis
>>> has the same issue (lose ReadIndex or silently ignore and the caller
>>> doesn't get response forever).
>>>
>>> Best,
>>> tison.
>>>
>>> [1] https://lists.apache.org/thread/wh9cf9456y1k3pt9v0qs9o3wychl937s
>>> [2]
>>> https://github.com/tikv/raft-rs/discussions/525#discussioncomment-6819686
>>>
>>>
>>>

Reply via email to