This project demonstrates workflow persistence and recovery using Quarkus Flow with Agentic LangChain4j across multiple persistence backends:
- File (MVStore)
- Redis
- PostgreSQL (JPA)
The objective is to validate that a workflow can resume execution from the last persisted step after a crash, without re-executing completed steps (saving high cost of calling again to the model).
The workflow is composed of three steps:
-
RiskClassifierAgent
- Invokes an LLM (via LangChain4j + Ollama)
- Produces a risk classification (
LOW,HIGH, …) - Persists the result
-
CrashOnceService
- Forces a JVM crash only once
- Simulates a failure after persistence
-
FormatterAgent
- Uses previously persisted data
- Produces the final result (
COMPLETED)
Expected execution:
- Workflow starts
- Step 1 completes and is persisted
- JVM crashes before Step 3
- Application restarts
- Workflow state from first agent is restored from persistence
- Execution resumes at Step 3
- Workflow completes successfully
- Java 21+
- Maven
- Docker
- Ollama running locally
Start required services:
docker compose up -dRun Ollama:
ollama serve
ollama pull llama3.2:3bBuild depending on the persistence type:
mvn clean package -Pfile -Dquarkus.profile=file
mvn clean package -Predis -Dquarkus.profile=redis
mvn clean package -Pjpa -Dquarkus.profile=jpaIn case of previous executions, be sure there's no crash marker first time:
rm -f target/agentic-crash-once.markerRun with desired persistence backend:
java -Dquarkus.profile=redis -jar target/quarkus-app/quarkus-run.jarjava -Dquarkus.profile=jpa -jar target/quarkus-app/quarkus-run.jarjava -Dquarkus.profile=file -jar target/quarkus-app/quarkus-run.jarcurl -X POST http://localhost:8080/workflow/order-agentic \
-H 'Content-Type: application/json' \
-d '{"orderId":"crash-1","amount":40,"customerId":"cust-1"}'First execution:
curl: (52) Empty reply from server
This is expected — the JVM crashes intentionally.
Restart the application:
java -Dquarkus.profile=<profile> -jar target/quarkus-app/quarkus-run.jarExpected logs after restart (there's no "[STEP 1 - RiskAgent] output=..."):
[STEP 2 - CrashOnce] recoveredInput=...
[STEP 3 - FormatterAgent] recoveredInput=...
[STEP 3 - FormatterAgent] output={..., status=COMPLETED}
- Key-based storage
- Fast recovery
- Example:
<instanceId>:do/0/riskAgent
-
Relational storage
-
Tables:
ProcessInstanceEntityTaskInfoEntity
quarkus.hibernate-orm.database.generation=update- Embedded persistence
- Stored locally on disk
- No external dependencies required
- Useful for lightweight recovery testing
redis-cli --scan --pattern '*:do/*'docker exec -it flow-postgres psql -U flow -d flowSELECT * FROM ProcessInstanceEntity;Inspect the data directory:
ls target/Look for MVStore files storing workflow state.
- Step-level persistence
- Crash recovery correctness
- No re-execution of completed steps
- Agentic workflow compatibility with persistence
- Consistency across different persistence backends
- First request crashes intentionally
- HTTP response is interrupted
- Recovery happens automatically on restart
This project demonstrates that:
Agentic workflows can be made fault-tolerant and resumable using Quarkus Flow persistence across Redis, JPA, and file-based storage.
