-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactoring the 'dirty'
event, dirtyPayload()
, and XXXPayloadDirty()
methods logic & spec
#158
Comments
Start working on this Issue |
Done. |
Paimon don't some features in wechat1.x (wechaty/puppet#158)
Currently in step1,the server remove payload cache.Why make it so complex? |
This complexity is necessary because the Wechaty Puppet Service Client also holds a cache layer, which means that if the Server only removed the payload cache for itself, the Client will not get notified and still hold the outdated (dirty) payload and use it, causing the end user problems like the |
The client call dirtyPayload method ,which means that the client can remove the cache. |
dirtyPayload is request-reponse .If the server remove some payload cache,the server will emit |
Yes, that's all the server needs to do: when it dirty any payload, emitting a Because the client can not know when they should remove a cache from the server, they must be notified by the server, with a That's all our current design is for, thank you very much for pointing this out. |
客户端请求payloadDirty的时候,就是客户端希望服务器端删除缓存,这个时候客户端请求成功之后就可以删除本地缓存了啊, 难道还需要服务器再发个event给客户端?是不是有点多此一举? 如果是服务器端自己根据逻辑判断需要删除缓存,那么服务器端会发送event给客户端的,我觉得0.x的设计是对的啊。 |
Thanks for raising this discussion! What you said is only partially true in the paimon use case because paimon is the most simple architecture in the wechaty ecosystem. In a complicated architecture like WXWork or Pad
This design is necessary for dealing with the dirty payloads to be correct. |
* downgrade wechaty to 0.68 Paimon don't some features in wechat1.x (wechaty/puppet#158) * Update paimon.md
能分别介绍一下,在Grpc服务端干啥工作? 第三步和第四步分别是啥?Grpc里面没有onDirty接口啊?第四步是啥意思?这个改动我没看明白,尤其是对于Grpc服务端正确的做法。 |
Hi @zpaimon Thanks for asking! This issue is written for the standard Wechaty Puppet Provider, so it has some Wechaty Puppet Abstract API implementation details, which might be confusing you because the Paimon is a pure gRPC service. I think from Paimon's perspective, what you need to care about is only the below two gRPC methods (and safely ignore all the 第三步和第四步 & Grpc里面没有onDirty接口 & 第四步):
You can read the detail of the above methods from the proto buffer definition at Wechaty gRPC Repo So here's the answer to your question: "在Grpc服务端干啥工作": you need to implement the below two algorithms: 1. send
|
Thank you very much!
I think paimon only has't the above logic and other things is ok! Is the design compatible with 0.x API ? If the client is 0.x and grpc server is 1.x,dead loop will happen. Client(DirtyPayload)->Server(dirtyPayload and send Dirty event to client)->Client(DirtyPayload)........ Dead loop ! |
You are welcome. Glad to know that you made it clear that what the Paimon need to follow, and please feel free to let me know if you have other questions. |
Is the design compatible with 0.x API ? If the client is 0.x and grpc server is 1.x,dead loop will happen. Client(DirtyPayload)->Server(dirtyPayload and send Dirty event to client)->Client(DirtyPayload)........ Dead loop ! |
Good catch: no, this design is not compatible with 0.x client 😢 It's a mistake because we have missed this problem when we were designing this v1 new logic. I believe this is also related to The solution for this problem might be:
Please feel free to use any other workarounds if you can find a better one. And I think this problem should be posted in the website if the user need to upgrade to wechaty v1.x go use the new paimon. |
So i think the GRPC protocol need to keep it simple logic.I suggest that the GRPC server don't send |
If the Wechaty user is using the But unfortunately, we can not follow your suggestion because what you suggested can not cover all the use cases. When we have any middle layers between the Wechaty and the Puppet Provider, for example:
Then we need a In this case, we need to emit the |
In the above pipeline,gRPC can await the |
Unfortunately, we can not only await the The Wechaty needs to make sure all the nodes in the pipeline have cleared the cache before it can continue reloading the payload again, to make sure there's no outdated cache in the pipeline so that it can get the newest data correctly. That's the reason why a Each node in the pipeline will listen to the
After the I hope I have explained this problem clearly. Please think more about a complex architecture other than the Paimon case, and that will greatly help you to understand this |
In other side,the design maybe trigger wechat risk control if infinite request for the fresh payload(Contact/Room)。 |
I think I might find the infinite loop problem you talked about in wechaty/puppet-wechat#177. I think it's a puppet issue so I post it here. Let's take a look at this example and see how it goes.
Wechaty Puppet ContactMixin contactPayloadDirty()
Puppet Server will emit a dirty event and resolve the above await. Everything seems alright so far, but here:
Wechaty Contact sync()
Wechaty Contact ready()
Here we go, we go back to Wechaty Puppet ContactMixin contactPayloadDirty() |
Good catch! It seems that the dead loop bug had been located. Will investigate on it after back to my computer, and please feel free to create a PR to send your fix. Thank you very much! |
There are many ways to fix this, so we should come to some consensus first. In my opinion, it's a bit odd for puppet-implementation to emit a dirty event when the dirty comes from the puppet-service. This dirty is completely different from the dirty event trigged by puppet-implementation. We can either remove it, or add a new event type for it. (like 'dirty-response'). |
If to separate these two kind of dirty event is not acceptable, we can also add a pool in onDirty handler, so that all dirty events started from __dirtyPayloadAwait will be dismissed. |
Maybe we should move this discuss into a new issue since this one is closed long ago. |
@huan is It means that the 'dirty' event depend on Implementation of the puppet? I use puppet-wxwork and create a room ,when it is created,here is the error,then I add something like
The error still exists,Is my calling method is not right?
|
Yes, it depends on the implementation. The new Wechaty v1 will expect a dirty event in this case. BTW: I think when using Wechaty, we should avoid calling the puppet directly as possible as we can. |
Working PRs
The New Design
In this new design, we will use:
'dirty'
event (added via add dirty rpc function definition for sync data grpc#79)dirtyPayload()
method (added via add dirtyPayload() and hide others #114)XXXPayloadDirty()
method (removed via add dirtyPayload() and hide others #114)With the logic:
'dirty'
event will be emitted by the deepest puppet by calling thedirtyPayload()
method:'dirty'
event with itsonDirty()
method, and invalid(remove) the cache of the payload with the specificid
andtype
(registeronDirty()
callback to thedirty
event will be done automatically by thePuppetAbstract
)XXXPayloadDirty()
should calldirtyPayload()
first, then wait the'dirty'
event. After receiving the'dirty'
event, it can return because the payload should be dirtied by the listener (which should be registered by the Puppet itself)'dirty'
event was lost, and throw an Error. (5 seconds in the current code)New Puppet API:
onDirty()
onDirty()
will beaddListener
to thedirty
event by thePuppetAbstract
super.onDirty()
!)Dirty a Payload Locally (in Puppet Provider)
How to dirty a payload (for puppet provider, locally):
puppet.XXXPayloadDirty(id)
future = await new Promise(resolve => puppet.once('dirty', resolve))
puppet.dirtyPayload('XXX', id)
setImmediate(() => puppet.emit('dirty', { type: 'XXX', id }))
<- add this task (emit) to the end of event loop queuepuppet.onDirty()
2. clean the cache
puppet.XXXPayloadDirty(id)
<- future resolvedawait new Promise(setImmediate)
<- wait current event loop task queue to be all executedDirty a Payload Remotely (with Puppet Service)
puppetService.XXXPayloadDirty(id)
future = await new Promise(resolve => puppet.once('dirty', resolve))
puppetService.dirtyPayload('XXX', id)
gRPC Service DirtyPayload
puppetServer.dirtyPayload('XXX', id)
puppetServer.emit('dirty', { type: 'XXX', id })
puppetServer.onDirty()
listener: remove payload cachegRPC Service Event (Stream)
puppetClient.emit('dirty', { type: 'XXX', id })
puppetClient.onDirty()
listener: remove payload cachepuppetClient.XXXPayloadDirty(id)
future resolvedSteps Mapping
Related issues
Breaking Change
This change will not affect the end-users.
However, the puppet provider developer needs to follow this new logic when they are using the new version of Puppet Abstract Class.
The Puppet Service will be affected too, so we recommend paying attention to the Wechaty versions to be matching on both the server and the client.
CC @wechaty/puppet @wechaty/grpc
The text was updated successfully, but these errors were encountered: