> 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.

I’ll fire a issue if we plan to implement it.

> 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.

IoTDB’s RaftServer facade is more like a customized client, rather than a 
server. It pipelines the caller’s requests and then forwards them to the Raft 
server sequentially. 

I think a `Connection` abstract inside which each request casually depends on 
all the previously requests is what you are asking for. A potential workaround 
is to implement a `Connection` which accepts async call but use `thenApply` 
internally to arrange these async calls. It can be officially implemented into 
Ratis if it’s a common demand.

Hope it helps,
William




> 2023年8月29日 07:14,tison <[email protected]> 写道:
> 
> > 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] <mailto:[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] <mailto:[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] 
>>> <mailto:[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] 
>>>>> <mailto:[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