Hi VaibhavTripathi,
Thanks for the detailed architecture — that context is genuinely helpful. The good news up front: moving from embedded Spring Boot to standalone WARs on Tomcat 10 changes nothing about how the Flowable engine behaves or what its tuning levers are. The async job executor, the shipped schema and indexes, the history configuration, and the database-coordinated job locking all work identically regardless of packaging. What changes is where you configure them — Tomcat container config and your JNDI DataSource instead of Spring Boot properties — not the underlying principles. So everything in my first reply still applies; below is how it maps onto your topology, plus the few things your distributed REST setup makes more important.
Container-managed JNDI connection pool (the part to get right)
Flowable fully supports a container-managed / JNDI DataSource — that’s a standard deployment mode, so no concern there. The thing to watch is that you described one shared pool serving both the Flowable engine webapp and the Spring facade. Those two have very different connection profiles competing for the same connections:
- The engine holds a connection for the duration of each running background job — every active async-executor thread occupies one connection while it works.
- The facade holds connections for its inbound REST request load.
So size the shared pool as: engine async-executor max threads + peak concurrent facade DB usage + headroom. If it’s under-sized, the symptom is requests and jobs stalling while they wait for a free connection (connection-wait / starvation), which looks like a slow system even though CPU is idle. Monitor active-vs-max connections and connection-wait time.
If the two services run as separate Tomcat instances, a clean and often safer option is to give each its own pool (each still pointing at the same database) so the facade’s request spikes can’t starve the engine’s job threads, and each can be sized for its own profile. A single shared pool is fine too — it just has to be sized for the sum of both workloads.
Also: the two SQL Server settings from my first reply still apply, now configured on the container-managed DataSource rather than a Spring Boot property:
- READ_COMMITTED_SNAPSHOT isolation level.
sendStringParametersAsUnicode=false on the JDBC URL.
Async executor on standalone Tomcat
The async executor still runs inside the engine webapp on Tomcat — it is active and you tune the same thread-pool size and queue capacity as before, just configured for this packaging. Remember the coupling: each executor thread needs one connection from the JNDI pool while it runs a job. So any time you raise the executor thread count, raise the pool to match (this is the most common cause of pool starvation in setups like yours).
The REST hop and outbound calls (most relevant to your topology)
The network hop between Flowable (port 80) and the facade (port 81) adds latency, but it does not change database tuning — the database remains the primary bottleneck.
What does deserve attention is outbound calls from the engine to the facade and onward to external client APIs. A synchronous outbound REST call made from a service/HTTP step holds the thread that is executing that step for the entire duration of the remote call — and if that step is running on the async executor, the thread also holds its database connection the whole time. Slow or unbounded external calls therefore throttle engine throughput and can exhaust the connection pool, even though the engine itself is doing nothing but waiting. Recommendations:
- Set aggressive connect and read timeouts on every outbound call so a slow remote endpoint can’t pin a thread indefinitely.
- Keep engine transactions short. Don’t do long external work inside the same transaction that touches the engine tables.
- Make outbound integration steps asynchronous (async continuations) so the engine commits and frees the thread, and the remote call happens on a controlled background thread rather than blocking the request that advanced the instance. Flowable’s HTTP-task and async-execution patterns are designed for exactly this.
- Bound your outbound HTTP client’s own connection pool so a burst of integration calls is back-pressured rather than spawning unbounded concurrency.
Tomcat connector thread pools
Size the HTTP connector thread pool (maxThreads) on both the engine connector (port 80) and the facade connector (port 81) for your expected concurrency, and keep them consistent with the JDBC pool — there’s no point accepting far more concurrent requests than you have database connections to service. Apply your standard JVM settings (-Xms/-Xmx or -XX:MaxRAMPercentage) to the Tomcat process.
Java 21 + Tomcat 10 is a perfectly good stack for Flowable 7.2. I’d suggest treating any virtual-threads experimentation as exactly that — validate it under load for your version before relying on it, rather than assuming it’s a free win for the engine’s job-executor and JDBC paths.
Clustering / scale-out is unchanged
Your standalone Tomcat nodes are stateless and coordinate only through the shared database, exactly as in my first reply — so you scale horizontally by adding more identical engine nodes against the one database, with the engine ensuring each background job runs on exactly one node. The same caveats hold: more nodes means more aggregate load and more connections on the shared database, so size the database for the total and make sure its max connections comfortably exceed the sum of all nodes’ pools.
Same public references
As before, Flowable doesn’t publish official sizing numbers for this engine, so the right approach is to apply these principles and then size from what you measure under realistic load. In your topology specifically, the two things most likely to bite are (1) the shared JNDI pool being under-sized for engine threads plus facade load, and (2) slow synchronous outbound calls pinning engine threads and connections — get those two right and the rest is standard tuning.
Best regards
Note: This Answer is AI generated and manually reviewed