|
| 1 | +## Prerequisites |
| 2 | + |
| 3 | +### Postgres SQL Server |
| 4 | + |
| 5 | +Install Postgres, then initilize and run the server. Something like: |
| 6 | + |
| 7 | +``` |
| 8 | +brew install postgresql |
| 9 | +initdb /usr/local/var/postgres -E utf8 |
| 10 | +pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start |
| 11 | +``` |
| 12 | + |
| 13 | +Create a test user and database: |
| 14 | + |
| 15 | +``` |
| 16 | +createuser springdemo |
| 17 | +createdb bootdemo |
| 18 | +psql bootdemo |
| 19 | +ALTER USER springdemo WITH PASSWORD 'springdemo'; |
| 20 | +\q |
| 21 | +``` |
| 22 | + |
| 23 | +### WildFly 8.1 |
| 24 | + |
| 25 | +Install WildFly 8.1 then configure add a postgres module: |
| 26 | + |
| 27 | +``` |
| 28 | +cd $JBOSS_HOME |
| 29 | +mkdir -p modules/org/postgresql/main |
| 30 | +wget http://jdbc.postgresql.org/download/postgresql-9.3-1102.jdbc41.jar |
| 31 | +mv postgresql-9.3-1102.jdbc41.jar modules/org/postgresql/main |
| 32 | +``` |
| 33 | + |
| 34 | +`$JBOSS_HOME/modules/org/postgresql/main/module.xml` |
| 35 | + |
| 36 | +``` |
| 37 | +<?xml version="1.0" encoding="UTF-8"?> |
| 38 | +<module xmlns="urn:jboss:module:1.0" name="org.postgresql"> |
| 39 | + <resources> |
| 40 | + <resource-root path="postgresql-9.3-1102.jdbc41.jar"/> |
| 41 | + </resources> |
| 42 | + <dependencies> |
| 43 | + <module name="javax.api"/> |
| 44 | + <module name="javax.transaction.api"/> |
| 45 | + </dependencies> |
| 46 | +</module> |
| 47 | +``` |
| 48 | + |
| 49 | +## Deployment |
| 50 | + |
| 51 | +The `$JBOSS_HOME/standalone/configuration/standalone-full.xml` is a good starting point, |
| 52 | +copy this file to `$JBOSS_HOME/standalone/configuration/standalone-spring-demo.xml` |
| 53 | + |
| 54 | +You need to register a PostgreSQL XA `Driver` and then configure an `xa-datasource`. |
| 55 | + |
| 56 | +Here's a complete listing of the `xa-datasource` contribution to the `datasources` |
| 57 | +element, and the `driver` contribution to the `drivers` element to configure a PostgreSQL |
| 58 | +DB connection to localhost. |
| 59 | + |
| 60 | +``` |
| 61 | +<datasources> |
| 62 | + ... |
| 63 | + <xa-datasource |
| 64 | + jndi-name="java:jboss/datasources/springdemo" |
| 65 | + pool-name="CrmXADS" |
| 66 | + enabled="true"> |
| 67 | + <xa-datasource-property name="url">jdbc:postgresql://localhost:5432/crm</xa-datasource-property> |
| 68 | + <driver>postgres</driver> |
| 69 | + <xa-pool> |
| 70 | + <min-pool-size>10</min-pool-size> |
| 71 | + <max-pool-size>20</max-pool-size> |
| 72 | + <prefill>true</prefill> |
| 73 | + </xa-pool> |
| 74 | + <security> |
| 75 | + <user-name>springdemo</user-name> |
| 76 | + <password>springdemo</password> |
| 77 | + </security> |
| 78 | + </xa-datasource> |
| 79 | + <drivers> |
| 80 | + ... |
| 81 | + <driver name="postgres" module="org.postgresql"> |
| 82 | + <xa-datasource-class>org.postgresql.xa.PGXADataSource</xa-datasource-class> |
| 83 | + </driver> |
| 84 | + </drivers> |
| 85 | +</datasources> |
| 86 | +``` |
| 87 | + |
| 88 | +Run Wildfly with the following command: |
| 89 | + |
| 90 | +``` |
| 91 | +$JBOSS_HOME/bin/standalone.sh -c standalone-boot-demo.xml |
| 92 | +``` |
| 93 | + |
| 94 | +Build the app and copy the war to `$JBOSS_HOME/standalone/deployments` |
| 95 | + |
| 96 | +## Replicating the issue |
| 97 | + |
| 98 | +Open a browser to http://localhost:8080/SPR-12118-1.0-SNAPSHOT/ and continually refresh |
| 99 | +the page. After a few refreshes things break: |
| 100 | + |
| 101 | +``` |
| 102 | +09:32:42,668 ERROR [io.undertow.request] (default task-24) UT005023: Exception handling request to /SPR-12118-1.0-SNAPSHOT/: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Could not open connection |
| 103 | + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:973) [spring-webmvc-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 104 | + at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852) [spring-webmvc-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 105 | + at javax.servlet.http.HttpServlet.service(HttpServlet.java:687) [jboss-servlet-api_3.1_spec-1.0.0.Final.jar:1.0.0.Final] |
| 106 | + at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837) [spring-webmvc-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 107 | + at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [jboss-servlet-api_3.1_spec-1.0.0.Final.jar:1.0.0.Final] |
| 108 | + at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final] |
| 109 | + at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:61) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final] |
| 110 | + at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final] |
| 111 | + at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78) |
| 112 | + at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final] |
| 113 | + at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:113) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final] |
| 114 | + at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:56) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final] |
| 115 | + at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final] |
| 116 | + at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:45) [undertow-core-1.0.15.Final.jar:1.0.15.Final] |
| 117 | + at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:61) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final] |
| 118 | + at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:58) [undertow-core-1.0.15.Final.jar:1.0.15.Final] |
| 119 | + at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:70) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final] |
| 120 | + at io.undertow.security.handlers.SecurityInitialHandler.handleRequest(SecurityInitialHandler.java:76) [undertow-core-1.0.15.Final.jar:1.0.15.Final] |
| 121 | + at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final] |
| 122 | + at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61) |
| 123 | + at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final] |
| 124 | + at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final] |
| 125 | + at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:240) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final] |
| 126 | + at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:227) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final] |
| 127 | + at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:73) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final] |
| 128 | + at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:146) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final] |
| 129 | + at io.undertow.server.Connectors.executeRootHandler(Connectors.java:177) [undertow-core-1.0.15.Final.jar:1.0.15.Final] |
| 130 | + at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:727) [undertow-core-1.0.15.Final.jar:1.0.15.Final] |
| 131 | + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [rt.jar:1.8.0_11] |
| 132 | + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [rt.jar:1.8.0_11] |
| 133 | + at java.lang.Thread.run(Thread.java:745) [rt.jar:1.8.0_11] |
| 134 | +Caused by: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Could not open connection |
| 135 | + at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763) [hibernate-entitymanager-4.3.6.Final.jar:4.3.6.Final] |
| 136 | + at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677) [hibernate-entitymanager-4.3.6.Final.jar:4.3.6.Final] |
| 137 | + at org.hibernate.jpa.internal.QueryImpl.getSingleResult(QueryImpl.java:524) [hibernate-entitymanager-4.3.6.Final.jar:4.3.6.Final] |
| 138 | + at com.test.wildflytransactions.AccountService.getCount(AccountService.java:41) [classes:] |
| 139 | + at com.test.wildflytransactions.AccountService$$FastClassBySpringCGLIB$$70f25f07.invoke(<generated>) [spring-core-4.1.0.BUILD-SNAPSHOT.jar:] |
| 140 | + at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) [spring-core-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 141 | + at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:717) [spring-aop-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 142 | + at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) [spring-aop-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 143 | + at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98) [spring-tx-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 144 | + at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:266) [spring-tx-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 145 | + at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) [spring-tx-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 146 | + at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 147 | + at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653) [spring-aop-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 148 | + at com.test.wildflytransactions.AccountService$$EnhancerBySpringCGLIB$$eea1167c.getCount(<generated>) [spring-core-4.1.0.BUILD-SNAPSHOT.jar:] |
| 149 | + at com.test.wildflytransactions.WebController.test(WebController.java:17) [classes:] |
| 150 | + at sun.reflect.GeneratedMethodAccessor33.invoke(Unknown Source) [:1.8.0_11] |
| 151 | + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [rt.jar:1.8.0_11] |
| 152 | + at java.lang.reflect.Method.invoke(Method.java:483) [rt.jar:1.8.0_11] |
| 153 | + at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215) [spring-web-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 154 | + at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132) [spring-web-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 155 | + at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) [spring-webmvc-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 156 | + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:781) [spring-webmvc-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 157 | + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:721) [spring-webmvc-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 158 | + at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83) [spring-webmvc-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 159 | + at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943) [spring-webmvc-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 160 | + at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877) [spring-webmvc-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 161 | + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961) [spring-webmvc-4.1.0.BUILD-SNAPSHOT.jar:4.1.0.BUILD-SNAPSHOT] |
| 162 | + ... 30 more |
| 163 | +Caused by: org.hibernate.exception.GenericJDBCException: Could not open connection |
| 164 | + at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:54) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 165 | + at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 166 | + at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:112) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 167 | + at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:235) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 168 | + at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.getConnection(LogicalConnectionImpl.java:171) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 169 | + at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.connection(StatementPreparerImpl.java:63) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 170 | + at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:162) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 171 | + at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:186) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 172 | + at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:160) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 173 | + at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1884) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 174 | + at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1861) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 175 | + at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1838) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 176 | + at org.hibernate.loader.Loader.doQuery(Loader.java:909) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 177 | + at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:354) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 178 | + at org.hibernate.loader.Loader.doList(Loader.java:2553) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 179 | + at org.hibernate.loader.Loader.doList(Loader.java:2539) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 180 | + at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2369) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 181 | + at org.hibernate.loader.Loader.list(Loader.java:2364) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 182 | + at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:496) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 183 | + at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:387) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 184 | + at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:231) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 185 | + at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1264) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 186 | + at org.hibernate.internal.QueryImpl.list(QueryImpl.java:103) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 187 | + at org.hibernate.jpa.internal.QueryImpl.list(QueryImpl.java:573) [hibernate-entitymanager-4.3.6.Final.jar:4.3.6.Final] |
| 188 | + at org.hibernate.jpa.internal.QueryImpl.getSingleResult(QueryImpl.java:495) [hibernate-entitymanager-4.3.6.Final.jar:4.3.6.Final] |
| 189 | + ... 54 more |
| 190 | +Caused by: java.sql.SQLException: javax.resource.ResourceException: IJ000453: Unable to get managed connection for java:jboss/datasources/bootdemo |
| 191 | + at org.jboss.jca.adapters.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:154) |
| 192 | + at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:139) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 193 | + at org.hibernate.internal.AbstractSessionImpl$NonContextualJdbcConnectionAccess.obtainConnection(AbstractSessionImpl.java:380) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 194 | + at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:228) [hibernate-core-4.3.6.Final.jar:4.3.6.Final] |
| 195 | + ... 75 more |
| 196 | +Caused by: javax.resource.ResourceException: IJ000453: Unable to get managed connection for java:jboss/datasources/bootdemo |
| 197 | + at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:440) |
| 198 | + at org.jboss.jca.core.connectionmanager.tx.TxConnectionManagerImpl.getManagedConnection(TxConnectionManagerImpl.java:422) |
| 199 | + at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.allocateConnection(AbstractConnectionManager.java:513) |
| 200 | + at org.jboss.jca.adapters.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:146) |
| 201 | + ... 78 more |
| 202 | +Caused by: javax.resource.spi.RetryableUnavailableException: IJ000653: The pool has been shutdown (BootPool,6c15203a) |
| 203 | + at org.jboss.jca.core.connectionmanager.pool.mcp.SemaphoreArrayListManagedConnectionPool.getConnection(SemaphoreArrayListManagedConnectionPool.java:355) |
| 204 | + at org.jboss.jca.core.connectionmanager.pool.AbstractPool.getTransactionNewConnection(AbstractPool.java:534) |
| 205 | + at org.jboss.jca.core.connectionmanager.pool.AbstractPool.getConnection(AbstractPool.java:435) |
| 206 | + at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:379) |
| 207 | + ... 81 more |
| 208 | +``` |
| 209 | + |
| 210 | + |
0 commit comments