Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 7, 2025

In microservice architectures using watchers with MongoDB adapters, updating policies in watcher callbacks triggers duplicate database writes and infinite update loops. When microservice A adds a policy, the watcher notifies B and C. If B and C use addPolicy() in their callbacks, they write to MongoDB again and trigger more watcher updates.

Changes

Internal methods (InternalEnforcer)

  • Added 6 *Self() methods that modify in-memory policy only: addPolicySelf, addPoliciesSelf, updatePolicySelf, removePolicySelf, removePoliciesSelf, removeFilteredPolicySelf
  • Skip adapter calls regardless of autoSave setting
  • Skip watcher calls regardless of autoNotifyWatcher setting
  • Preserve role link updates for grouping policies

Public API (ManagementEnforcer)

  • Exposed 26 public self* methods matching existing policy API surface
  • Policy operations: selfAddPolicy, selfAddPolicies, selfUpdatePolicy, selfRemovePolicy, etc.
  • Grouping operations: selfAddGroupingPolicy, selfAddGroupingPolicies, selfUpdateGroupingPolicy, etc.
  • Named and filtered variants for both

Usage

// In watcher callback - synchronize without triggering adapter/watcher
watcher.setUpdateCallback(async (msg) => {
  if (!await enforcer.hasPolicy(...msg.params)) {
    await enforcer.selfAddPolicy(...msg.params);  // In-memory only
  }
});

// Normal operations still use regular methods
await enforcer.addPolicy('alice', 'data1', 'read');  // Saves to DB, notifies watcher

Testing

Added 28 tests verifying adapter and watcher methods are not invoked when autoSave=true and autoNotifyWatcher=true.

Original prompt

This section details on the original issue you should resolve

<issue_title>Feature request: Methods in enforcer for adding, updating and deleting policies without usage of adapter (even if autoSave is true)</issue_title>
<issue_description>My environment/setup:

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

For my use case I need methods in the enforcer for adding, updating and deleting policies only inside the enforcer object itself - even if an adapter is set and autoSave is true.

I need these functions, to be able to call them in the updateCallback of the watcher of all microservices, because I need the watcher to keep the policy up to date across all microservices. With the currently available methods its not possible to do this without any performance issues or creating duplicates of policies in the MongoDB collection.

This issue results out of a PR (#505) for discussion.

The methods, that I added, are for me for calling inside the updateCallback() of the watcher.

Here is a detailed example to better understand my problem:

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. So what I need are functions like selfAddPolicy(), selfRemovePolicy(), and so on that not only don't call watcher.update() but also don't call the adapter functions on policy changes.

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

</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@hsluoyz hsluoyz marked this pull request as ready for review December 7, 2025 17:25
Copilot AI changed the title [WIP] Add methods to enforcer for policy management Add self-only policy methods for watcher synchronization without adapter calls Dec 7, 2025
Copilot AI requested a review from hsluoyz December 7, 2025 17:38
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.

Feature request: Methods in enforcer for adding, updating and deleting policies without usage of adapter (even if autoSave is true)

3 participants