Skip to content

Conversation

@yiev
Copy link

@yiev yiev commented Sep 24, 2025

I got the following situation:

  • Microservice architecture
  • using casbin with casbin-mongoose-adapter (SyncedAdapter) and @casbin/mongo-changestream-watcher
  • autoSave is true

Now when microservice A adds a new policy via the addPolicy() method of the Enforcer the following happens:

  • All other microservices become a notification via the mongodb changestream watcher
    • now here I want to add the new policy only to the locally held policy and NOT via the adapter (as it already exists in the MongoDB). With the currently available methods I can only add the policy triggering an endless loop (adding more and more duplicates of the already existing policy) OR I can reload the complete policy on each received change by the watcher, but that's not really a good idea if you got several hundred (micro)services that try this at the same time on every change to the policy.

The situation is also similar for updating or deleting policies with the described setup and configuration.

I need methods for adding, updating and deleting policies without persisting them via the adapter - even if autoSave is true.

So I created this pull request here. 😃 I would be glad if a maintainer could have a look on it. 😃

@yiev
Copy link
Author

yiev commented Sep 24, 2025

In my own repo the checks were all successful:
https://github.com/yiev/node-casbin/actions/runs/17973449411

Don't know why coveralls is failing here...

@hsluoyz
Copy link
Member

hsluoyz commented Sep 24, 2025

@yiev then don't use watcher, why use watcher and then disable it?

@yiev
Copy link
Author

yiev commented Sep 24, 2025

@hsluoyz Thank you for your really fast response! 😸 I need the watcher to keep the policy up to date across all microservices.
The methods, that I added, are for me for calling inside the updateCallback() of the watcher.

Maybe it gets a bit clearer with a bit more detailed example of what happens right now:
Lets say we got three microservices (A, B and C), all three can interact with the casbin policy (reading, adding, updating, deleting policies).

Microservice A adds a new policy via enforcer.addPolicy(). This leads to a new document in the MongoDB collection and that creates a change event on the MongoDB change stream.

So the updateCallback function of the watcher in all three microservices is called. In this function I could just reload the whole policy via enforcer.loadPolicy(). This would work! BUT that is performance wise a bad idea if we got severval hundred microservices doing that all the time.
So in the updateCallback function of all three microservices I check instead first:

  • is the newly added policy already part of the policy of the enforcer?
    • in microservice A it is already part of the policy, so we don't do anything further
    • in microservice B and C the newly added policy is not part of the policy:
      • I call enforcer.addPolicy() to add it to the policy of the enforcer of microservice B and C. But enforcer.addPolicy() also adds the policy via the adapter by calling adapter.addPolicy(). So at this point I am creating duplicates of the new policy in the MongoDB colleciton. And enforcer.addPolicy() also calls the watcher.update() again which is just unnecessary but causing no issues.

So I wanted a method for adding policies, that does not call adapter.addPolicy() and also does not call watcher.update(). The same for updating and deleting policies.

I hope now it makes sense, why I want these newly added methods? Or am I missing something and my approach does not make any sense?

@hsluoyz
Copy link
Member

hsluoyz commented Sep 24, 2025

@yiev first create issue to discuss before making PR

@hsluoyz
Copy link
Member

hsluoyz commented Sep 29, 2025

@yiev don't do code formatting in this same PR. Did your solution come from Golang Casbin?

@yiev
Copy link
Author

yiev commented Sep 30, 2025

@hsluoyz
Thanks for your response. I just reverted the code formatting in my branch (yiev:pr-add-del-update-without-adapter-usage) this PR is related to.

No, the solution was my own idea. I didn't work with Golang Casbin yet, but I just checked the repo of Golang Casbin and the same behaviour is given there also (at least as far as I can say from just looking for some minutes into it). So I would have the same problem with Golang Casbin with my architecture.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants