-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Narayana Transaction Logs QuickStart #1311
Add Narayana Transaction Logs QuickStart #1311
Conversation
Hey @zhfeng , WDYT? I decided against your project with dummy xa resources because it feels like it is important to show automatic recovery on XA resources registered by Agroal. I can make adjustments based on your feedback. Recovery works, but as you can see here
com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule to see XA resource. Without that, transaction never recovers and you can see
I presume it is not a bug, but rather something I am missing? Anyway, feedback is welcome and I think even with that additional request, this quickstarts fill its purpose. also cc @mmusgrov (review welcomed) |
This comment has been minimized.
This comment has been minimized.
Thanks @michalvavrik and it smells like a regression bug. IIRC, it should work before. And can you check it with Quarkus 3.2 ? I think there are some changes with If that is the case, please can you open an issue on |
Hey @zhfeng , fun fact, it only works with
However you were right, it goes down to the datasource initialization. I've added following workaround that fixed problem:
Thanks! I'll open issue, your quick response is well appreciated. |
35c09de
to
861b2fa
Compare
Upstream issue is now resolved, so I adjusted quickstart bit. Should be ready for review. cc @zhfeng |
This comment has been minimized.
This comment has been minimized.
861b2fa
to
c445b93
Compare
This comment has been minimized.
This comment has been minimized.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM !
Good cooperation @michalvavrik and @zhfeng ! |
c445b93
to
289fcb9
Compare
This comment has been minimized.
This comment has been minimized.
I rebased on current development. CI fails on "Cache maven Repository", so I doubt it can be related. |
289fcb9
to
d922652
Compare
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi. Thanks for working on this. I'll give my review, though I may be lacking some context as I found a few things quite unexpected for a quickstart... Hope the review is not too irrelevant.
services: | ||
postgres: | ||
container_name: narayana-transaction-logs-database | ||
image: "postgres:latest" | ||
restart: always | ||
volumes: | ||
- ./init-script.sql:/docker-entrypoint-initdb.d/init-script.sql | ||
command: "--max_prepared_transactions=100" | ||
environment: | ||
POSTGRES_USER: "quarkus_test" | ||
POSTGRES_PASSWORD: "quarkus_test" | ||
POSTGRES_DB: "narayana_transaction_logs_db" | ||
ports: | ||
- "5432:5432" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't this just be configured as a dev service?
FWIW there are options to customize dev mode, in particular:
- the command: https://quarkus.io/guides/databases-dev-services#quarkus-datasource-config-group-dev-services-build-time-config_quarkus.datasource.devservices.command
- volumes: https://quarkus.io/guides/databases-dev-services#mapping-volumes-into-dev-services-for-database
It feels weird that we boast about Quarkus being easy to use in dev mode everywhere, and then we force people to use docker-compose in a quickstart...
CREATE TABLE quarkus_jbosststxtable (statetype integer not null, hidden integer not null, typename character varying(255) not null, uidstring character varying(255) not null, objectstate bytea); | ||
CREATE TABLE quarkus_jbosststxtable_historical_data AS SELECT * FROM quarkus_jbosststxtable; | ||
CREATE OR REPLACE FUNCTION object_store_historical_data() RETURNS TRIGGER AS ' | ||
BEGIN | ||
INSERT INTO quarkus_jbosststxtable_historical_data(statetype, hidden, typename, uidstring, objectstate) VALUES (NEW.statetype, NEW.hidden, NEW.typename, NEW.uidstring, NEW.objectstate); | ||
RETURN NULL; | ||
END; | ||
' LANGUAGE plpgsql; | ||
CREATE TRIGGER object_store_historical_data_trigger AFTER INSERT ON quarkus_jbosststxtable FOR EACH ROW EXECUTE FUNCTION object_store_historical_data(); | ||
CREATE TABLE audit_log (id BIGSERIAL PRIMARY KEY, message VARCHAR(10), datasource VARCHAR(40)); | ||
CREATE TABLE example (data VARCHAR(6) NOT NULL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the quickstart at least, can't we rely on automatic creation? I thought I saw this feature somewhere...
@GET | ||
@Produces(MediaType.TEXT_PLAIN) | ||
@Transactional | ||
@Path("/annotation-way") | ||
public boolean annotationWay(@RestQuery boolean rollback) throws SystemException { | ||
var result = makeTransaction(); | ||
if (rollback) { | ||
transactionManager.getTransaction().setRollbackOnly(); | ||
} | ||
return result; | ||
} | ||
|
||
@GET | ||
@Produces(MediaType.TEXT_PLAIN) | ||
@Path("/static-transaction-manager-way") | ||
public boolean staticTransactionManagerWay(@RestQuery boolean rollback) throws SystemException, NotSupportedException, | ||
HeuristicRollbackException, HeuristicMixedException, RollbackException { | ||
com.arjuna.ats.jta.TransactionManager.transactionManager().begin(); | ||
var result = makeTransaction(); | ||
if (rollback) { | ||
com.arjuna.ats.jta.TransactionManager.transactionManager().rollback(); | ||
} else { | ||
com.arjuna.ats.jta.TransactionManager.transactionManager().commit(); | ||
} | ||
return result; | ||
} | ||
|
||
@GET | ||
@Produces(MediaType.TEXT_PLAIN) | ||
@Path("/injected-transaction-manager-way") | ||
public boolean injectedTransactionManagerWay(@RestQuery boolean rollback) throws SystemException, NotSupportedException, | ||
HeuristicRollbackException, HeuristicMixedException, RollbackException { | ||
transactionManager.begin(); | ||
var result = makeTransaction(); | ||
if (rollback) { | ||
transactionManager.rollback(); | ||
} else { | ||
transactionManager.commit(); | ||
} | ||
return result; | ||
} | ||
|
||
@GET | ||
@Produces(MediaType.TEXT_PLAIN) | ||
@Path("/injected-user-transaction-way") | ||
public boolean injectedUserTransactionWay(@RestQuery boolean rollback) throws SystemException, NotSupportedException, | ||
HeuristicRollbackException, HeuristicMixedException, RollbackException { | ||
userTransaction.begin(); | ||
var result = makeTransaction(); | ||
if (rollback) { | ||
userTransaction.rollback(); | ||
} else { | ||
userTransaction.commit(); | ||
} | ||
return result; | ||
} | ||
|
||
@GET | ||
@Produces(MediaType.TEXT_PLAIN) | ||
@Path("/static-user-transaction-way") | ||
public boolean staticUserTransactionWay(@RestQuery boolean rollback) throws SystemException, NotSupportedException, | ||
HeuristicRollbackException, HeuristicMixedException, RollbackException { | ||
com.arjuna.ats.jta.UserTransaction.userTransaction().begin(); | ||
var result = makeTransaction(); | ||
if (rollback) { | ||
com.arjuna.ats.jta.UserTransaction.userTransaction().rollback(); | ||
} else { | ||
com.arjuna.ats.jta.UserTransaction.userTransaction().commit(); | ||
} | ||
return result; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure showing uses of UserTransaction
has value in a quickstart, in particular when our documentation calls that the "legacy approach".
Using the transaction manager directly also seems a bit odd.
IMO if we want to showcase multiple ways of handling transactions, we should go with @Transaction
and QuarkusTransaction
.
public void deleteObjectStoreHistoricalData() { | ||
// delete content of database table where we back up each insert into JDBC object store | ||
// it is only necessary for our tests so that we can run every test against clean table |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe call this testCleanup
and make it a @Startup
method, to avoid any misunderstanding and remove confusing statements from tests?
public class CrashingXAConnection extends PGXAConnection { | ||
|
||
private static final Logger LOG = Logger.getLogger(CrashingXAConnection.class); | ||
private final PGXAConnection delegate; | ||
private volatile InstanceHandle<RoutingContext> routingContextInstanceHandle; | ||
|
||
CrashingXAConnection(PGXAConnection delegate, BaseConnection connection) throws SQLException { | ||
super(connection); | ||
this.delegate = delegate; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a quickstart, supposed to give people an example of how to use Narayana in their Quarkus application.
So, I don't think this kind of test fixture has its place here. Why would someone need a crashing connection in their project?
I get that we need to demonstrate that transactions work correctly somehow, but... isn't there a simpler approach, one that we could find in an actual application? Wouldn't it be enough to throw some exception during a transaction started with @Transaction
/QuarkusTransaction
?
import java.sql.Connection; | ||
import java.sql.SQLException; | ||
|
||
public class CrashingPGXADataSource extends PGXADataSource { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as for CrashingXAConnection
, I'm not sure a quickstart is the right place for this kind of things.
# configure 2 datasources with enabled XA transactions | ||
quarkus.datasource.db-kind=postgresql | ||
quarkus.datasource.username=quarkus_test | ||
quarkus.datasource.password=quarkus_test | ||
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost/narayana_transaction_logs_db | ||
quarkus.datasource.jdbc.transactions=xa | ||
quarkus.datasource.jdbc.driver=org.acme.quickstart.recovery.CrashingPGXADataSource | ||
quarkus.datasource.other-ds.jdbc.transactions=${quarkus.datasource.jdbc.transactions} | ||
quarkus.datasource.other-ds.db-kind=${quarkus.datasource.db-kind} | ||
quarkus.datasource.other-ds.username=${quarkus.datasource.username} | ||
quarkus.datasource.other-ds.password=${quarkus.datasource.password} | ||
quarkus.datasource.other-ds.jdbc.url=${quarkus.datasource.jdbc.url} | ||
quarkus.datasource.other-ds.jdbc.driver=${quarkus.datasource.jdbc.driver} | ||
|
||
# configure transaction manager to use JDBC object store and enable automatic recovery | ||
quarkus.transaction-manager.node-name=quarkus-quickstart | ||
quarkus.transaction-manager.object-store.type=jdbc | ||
quarkus.transaction-manager.object-store.datasource=object-store-ds | ||
quarkus.transaction-manager.enable-recovery=true | ||
|
||
# datasource used for the JDBC object store must have disabled transactions | ||
quarkus.datasource.object-store-ds.jdbc.transactions=disabled | ||
quarkus.datasource.object-store-ds.db-kind=${quarkus.datasource.db-kind} | ||
quarkus.datasource.object-store-ds.username=${quarkus.datasource.username} | ||
quarkus.datasource.object-store-ds.password=${quarkus.datasource.password} | ||
quarkus.datasource.object-store-ds.jdbc.url=${quarkus.datasource.jdbc.url} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This config can be simplified a lot if you rely on dev services (see my other comment on that topic).
- build application: | ||
- `./mvnw clean package -DskipTests -DskipITs` | ||
- remove previous database volumes, so that we start with a clean sheet: | ||
- `docker-compose down --volumes` | ||
- start database: | ||
- `docker compose up` | ||
- wait until container has started and is ready to accept connections | ||
- start application: | ||
- `java -jar ./target/quarkus-app/quarkus-run.jar` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could be summed up as ./mvnw clean compile quarkus:dev
if you used dev services.
d922652
to
ed41050
Compare
This PR needs to be adapted to changes in Quarkus and Quakurs Quickstarts that happen in between. I'll do it later this week, address, Yoann comments and reopen it. |
closes: #1306
Ad Check list: The guide update is being worked on by docs team right now and I don't want to interfere. If this ever pass review, I'll check with @MichalMaler how to proceed.
Check list:
Your pull request:
development
branch999-SNAPSHOT
version of Quarkusmvn clean test
)mvn clean package -Pnative
)mvn clean verify -Pnative
)README.md
file (with build and run instructions)pom.xml
andREADME.md