hsluoyz commented on issue #418:
URL: https://github.com/apache/casbin-rs/issues/418#issuecomment-5206943489

   @martinadamsdev 
   
   Thanks for the well-structured proposal — this is a direction we're 
interested in. Answering your four questions, then raising the two design 
points that I think decide whether this succeeds.
   
   **1. Is it useful?**
   
   Yes. `enforce()` is the hot path for most users, and for large policy sets 
pure-Python pycasbin is dominated by matcher evaluation and policy iteration. 
Moving that into Rust is a real, order-of-magnitude-class win. Before we 
commit, though, I'd like to see a benchmark against pycasbin on a realistic 
workload (e.g. RBAC with role hierarchy, ~10k policies, ~100k `enforce()` 
calls) so we're arguing from numbers rather than from analogy with polars/ruff.
   
   **2. Which repo?**
   
   A separate repo, not `casbin-rs`. The `casbin` crate is a plain library and 
should stay dependency-clean; PyO3 pulls in the CPython ABI and pins a very 
different CI/release story (cibuildwheel matrix, abi3, PyPI trusted publishing) 
from `cargo publish`. Keeping them separate also lets the binding pin a 
released `casbin` version instead of tracking `master`.
   
   Also note the PyPI name `casbin` is already taken by pycasbin, so the 
package will need its own name.
   
   **3. Which APIs first?**
   
   Roughly in this order:
   
   - `CoreApi`: `new_enforcer`, `enforce`, `enforce_mut`, `load_policy`, 
`save_policy`, `enable_auto_save`, `enable_enforce`
   - Model/policy loading: from file paths and from strings (`FileAdapter`, 
`MemoryAdapter`, `StringAdapter`)
   - `ManagementApi`: `add_policy` / `add_policies` / `remove_policy` / 
`remove_filtered_policy` / `get_policy` / `get_filtered_policy` and the 
`g`-rule equivalents
   - `RbacApi`: `add_role_for_user`, `delete_role_for_user`, 
`get_roles_for_user`, `get_users_for_role`, `has_role_for_user`, 
`get_implicit_*`
   - Then the optional features that matter in production: `cached` 
(CachedEnforcer), `glob`, `ip`, `explain`
   
   `Watcher`, `RoleManager` and custom matcher functions can come later — see 
below for why they're the tricky ones.
   
   **4. Interested in collaborating?**
   
   Yes, but I'd like a short design note (or a PoC branch) on the two points 
below before we bless a repo under the org, because they determine how much of 
the promised speedup actually survives.
   
   ---
   
   ### Design point A — the async boundary
   
   casbin-rs is async where the adapter is involved and sync where it isn't:
   
   - `Enforcer::new` and `load_policy` are `async` (`src/enforcer.rs:528`, 
`src/enforcer.rs:817`)
   - `enforce` / `enforce_mut` are **sync** (`src/enforcer.rs:673`, 
`src/enforcer.rs:773`)
   - the `Adapter` trait is fully async (`src/adapter/mod.rs:26`)
   
   So the binding has to decide: drive a private Tokio runtime and `block_on` 
inside the constructor and policy-mutating calls, giving Python a fully sync 
API that mirrors pycasbin; or expose `async def` via pyo3-async-runtimes. My 
recommendation is sync-first — it's what pycasbin users expect and what makes 
the binding a drop-in — with an async API considered later. Whichever you pick, 
`enforce()` should release the GIL (`Python::allow_threads`) so multi-threaded 
servers get true parallelism; that's a second, less obvious win over pycasbin 
that's worth designing in from the start.
   
   ### Design point B — the adapter ecosystem (the make-or-break one)
   
   A large share of pycasbin's value isn't the enforcer, it's the adapter 
ecosystem: SQLAlchemy, Django ORM, MongoDB, Redis, and so on. A Rust-core 
enforcer can't use any of them for free, and this is the part the proposal 
doesn't address.
   
   Two options:
   
   1. **Rust adapters only.** Fastest and simplest, but users have to give up 
their existing storage integration — which is exactly the reason most of them 
adopted pycasbin. This makes the binding a niche tool.
   2. **Bridge Python adapters into the Rust `Adapter` trait.** A Rust shim 
implements `Adapter` and calls back into a Python object under the GIL. Users 
keep every existing pycasbin adapter, and — importantly — the callbacks land on 
`load_policy` / `save_policy` / `add_policy`, which are startup or write-path 
operations, while `enforce()` stays 100% Rust. That's the right place to pay 
the cost.
   
   I'd go with (2), and treat (1) as an optimization for users who don't need 
the ecosystem.
   
   The same GIL-callback question applies to `Watcher`, custom `RoleManager` 
implementations, and custom matcher functions. Custom functions are the one 
case where the answer should probably be "don't": a Python function called from 
the Rhai matcher on every request would run once per policy rule per 
`enforce()` call and would erase the speedup. If they're supported at all, that 
cost needs to be documented loudly.
   
   ---
   
   If you're up for it, a PoC covering `new_enforcer` + `enforce` + a 
Python-implemented adapter, with a benchmark against pycasbin, would be enough 
to settle both questions and decide whether we move it under the org.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to