This application is a demo for using JBoss Narayana as a transaction manager in Ruby via Narayana REST API (rts subsystem). There are two transaction types implemented, nested and chained. The whole environment and application is packaged into Docker containers.
- docker
- docker-compose
$ docker-compose up
The first time, this may take a long time as we need to download Ruby base image, PostgreSQL and Wildfly application server and
build the application. Descriptions about each Docker container is available at docker-compose.yml
file.
Narayana transaction manager is a part of Wildfly application server and can be used via its REST API When rts
subsystem is enabled in Wildfly. Documentation about how rts
API works and interacts is available at http://narayana.io/docs/project/index.html#d0e15500.
There were no implementation of Narayana API client in Ruby so I created it. Communication
with Narayana is done through narayana/transaction
class and HTTP requests. It can create a new transaction,
enlist new participants and put or retrieve the transaction status.
Here is a sample code of how to use Transaction
class.
tx = Transaction.new # Asks for a new transaction on Wildfly
tx.participate @resource # Enlists resource in transction
tx.status # TransactionActive
tx.commit # Puts new status (TransactionCommitted) to Narayana for this transaction
I created a Task model using DataMapper
ORM, and created the Task resource using Sinatra
to respond to
API calls from Wildfly server according to Narayana documentation. Each task can be type of Chained or Nested.
Chained tasks can have one next and one prev task. When a chained task is commited, its next task is
triggered. If one chained task fails, itself and its prev are rolled back.
Nested tasks can have multiple childs. When a nested task is commited, all of its children are triggered and commited in one transaction, all or none.
Using this Task model, we can test our transaction manager. Here is a sample code.
Chained transactions:
t1 = CheindTask.create
t2 = CheindTask.create
# Creating a chain
t1.next = t2
t1.save
t1.commit # Commits t1 and then t2
Nested transactions:
t1 = NestedTask.create
t2 = NestedTask.create
t3 = NestedTask.create fails: true
# Creating nested transaction
t1.childs << t2 # Add t2 to childs
t1.childs << t3
t1.save
t1.commit # Commits t1, then t2 and t3 in one transaction
Two sample scenarios are tested as a client in the app/client.rb
file, one chained transaction and one nested.
These scenarios are run in a separate process called client
. Also we have another process called service
which
is our server talking to Narayana API on Wildfly server. All interactions are logged to console. A sample log
file is provided with the source code in sample.log
. Here is an example log.
The first column says that logs are from the Ruby container or Narayana container. The second column shows the name of the process, client or service.
The results can be seen in the log file or reproduced by running the application.
The first scenario is a nested transaction as picture below. Transaction 4 aborts. As a result the root is commited, all siblings of T4 are rolled back, because they were in a transaction. Othere are not triggered because their parent has aborted.
The second scenario is a chained transaction as picture below. C2 aborts. Chain stops. C2 and its previous tasks should be rolled back in reverse order. Its next transaction is not triggered.
The whole application is in Ruby programming language. It uses Sinatra
and Rack
for serving the API client.
It uses DataMapper
as ORM for creating the Task model. PostgreSQL
is used as our database for storing tasks.
The processes are described in the file Procfile
and are managed by foreman
to run as daemons.
Each line in the Procfile
describes one process of the application.
The application uses four docker images, postgres, wildfly-rts, dnsdock and ruby.
The Ruby image is the main image
for our application which is built by the provided Dockerfile
. These images are in the public Docker hub registry
and are downloaded and built automatically by the above command on the first time. The image dnsdock is used a DNS server
between containers so that they can find each other.
These images are configured and run using docker-compose
. The configuration is in the docker-compose.yml
file.