Uploaded image for project: 'Confluence Data Center'
  1. Confluence Data Center
  2. CONFSERVER-35465

AbstractJob should take care of transaction management as suggested by it's documentation

      Raised from https://developer.atlassian.com/display/CONFDEV/Preparing+for+Confluence+5.7?focusedCommentId=30612993#comment-30612993

      Since 5.7 we are experiencing Hibernate Session exceptions when accessing CEOs in a Job context (<job /> module in plugin.xml)

      ERROR [scheduler_Worker-3] [sf.hibernate.proxy.LazyInitializer] initializeWrapExceptions Exception initializing proxy net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed
      ...
      at com.atlassian.confluence.core.ConfluenceEntityObject.getCreatorName(ConfluenceEntityObject.java:27)
      

      The documentation says:

      At a minimum, the module class must implement Quartz's Job interface, but for access to Confluence's objects and database you should extend com.atlassian.quartz.jobs.AbstractJob.

      But simply extending AbstractJob is not enough. There is no transaction management automatically applied in this case.

            [CONFSERVER-35465] AbstractJob should take care of transaction management as suggested by it's documentation

            Atlassian Update - 14 April 2025

            Hi,

            At Atlassian, our goal is to ensure we’re providing the best experience for our customers. With our new Data Center strategy, Atlassian's focus is on security, compliance, and performance and is a key driver in prioritizing bugs. Closing the bugs that do not fall into those categories will allow us to focus on the ones in the most current versions of our products.

            This bug is being closed due to a lack of engagement in the last four years, including no new watchers, votes, or comments; this inactivity suggests a low impact.

            Please note the comments on this thread are not being monitored.

            You can read more about our bug fix policy here and how we prioritize bugs.

            To learn more about our recent investments in Confluence Data Center, please check our public roadmap and dashboards containing recently resolved issues, current work, and future plans.

            Kind regards,
            Confluence Data Center

            George Varghese added a comment - Atlassian Update - 14 April 2025 Hi, At Atlassian, our goal is to ensure we’re providing the best experience for our customers. With our new Data Center strategy, Atlassian's focus is on security, compliance, and performance and is a key driver in prioritizing bugs. Closing the bugs that do not fall into those categories will allow us to focus on the ones in the most current versions of our products. This bug is being closed due to a lack of engagement in the last four years , including no new watchers, votes, or comments; this inactivity suggests a low impact. Please note the comments on this thread are not being monitored. You can read more about our bug fix policy here and how we prioritize bugs. To learn more about our recent investments in Confluence Data Center, please check our public roadmap and dashboards containing recently resolved issues , current work, and future plans . Kind regards, Confluence Data Center

            Brett Ryan added a comment -

            A query on the phrase to the document linked, I refer to this phrase:

            The core Confluence services and managers are declaratively wrapped so individual calls to any of these services do not require you to perform explicit transaction management. However, you may need to manually wrap your job execution in a transaction if -

            1. You are making use of your own components which are not declaratively wrapped in a transaction via Spring configuration.
            2. You are sharing data between a number of different calls to one or more services

            I'm a touch confused by this. From this phrase it would suggest that any calls to confluence services do not require the transaction management, yet unfortunately they do.

            While I can retrieve the Attachment}}s, querying certain attributes like {{getSpace() result in the reported exception. As does the call to AttachmentManager.removeAttachmentVersionFromServer.

            Brett Ryan added a comment - A query on the phrase to the document linked , I refer to this phrase: The core Confluence services and managers are declaratively wrapped so individual calls to any of these services do not require you to perform explicit transaction management. However, you may need to manually wrap your job execution in a transaction if - You are making use of your own components which are not declaratively wrapped in a transaction via Spring configuration. You are sharing data between a number of different calls to one or more services I'm a touch confused by this. From this phrase it would suggest that any calls to confluence services do not require the transaction management, yet unfortunately they do. While I can retrieve the Attachment}}s, querying certain attributes like {{getSpace() result in the reported exception. As does the call to AttachmentManager.removeAttachmentVersionFromServer .

            Brett Ryan added a comment -

            I'm facing this problem in a plugin I wrote that took advantage of the AttachmentManager#removeAttachmentVersionFromServer(Attachment) API that was introduced in 4.3, this was something we direly needed to be able to delete older versions of attachments to free up space.

            As of 5.7 this started failing and I have just started looking at it. The old version used to process some 30,000 attachments on the site in around 40 seconds, now, it takes about 20 minutes to do this with the delete function turned off.

            My code is effectively exactly the same, but now wrapped in TransactionTemplate.execute, with the delete turned on which basically just enables calls to AttachmentManager#removeAttachmentVersionFromServer(Attachment) produces a runtime exception that's swallowed before it gets to me so the call somewhat succeeds but produces a enough logged exceptions that if it were pot it would keep Cheech and Chong happy for their whole lives.

            If I dumb down my code, it kinda looks like this (available on github).

                @Override
                public void doExecute(JobExecutionContext jec) throws JobExecutionException {
                    transactionTemplate.execute(new TransactionCallback<Object>() {
                        @Override
                        public Object doInTransaction() {
                            Iterator<Attachment> i = attachmentManager.getAttachmentDao().findLatestVersionsIterator();
                            while (i.hasNext()) {
                                Attachment att = i.next();
                                if (att.getVersion() > 1 && att.getSpace() != null) {
                                    List<Attachment> prior = attachmentManager.getPreviousVersions(a);
                                    // remove newest two versions from prior.
                                    for (Attachment p : toDelete) {
                                        attachmentManager.removeAttachmentVersionFromServer(p);
                                    }
                                }
                            }
                        }
                    });
                }     
            

            So what worked in and up to 5.6 is now screwed on me. Any ideas on what I now need to do?

            Brett Ryan added a comment - I'm facing this problem in a plugin I wrote that took advantage of the AttachmentManager#removeAttachmentVersionFromServer(Attachment) API that was introduced in 4.3, this was something we direly needed to be able to delete older versions of attachments to free up space. As of 5.7 this started failing and I have just started looking at it. The old version used to process some 30,000 attachments on the site in around 40 seconds, now, it takes about 20 minutes to do this with the delete function turned off. My code is effectively exactly the same, but now wrapped in TransactionTemplate.execute , with the delete turned on which basically just enables calls to AttachmentManager#removeAttachmentVersionFromServer(Attachment) produces a runtime exception that's swallowed before it gets to me so the call somewhat succeeds but produces a enough logged exceptions that if it were pot it would keep Cheech and Chong happy for their whole lives. If I dumb down my code, it kinda looks like this ( available on github ). @Override public void doExecute(JobExecutionContext jec) throws JobExecutionException { transactionTemplate.execute( new TransactionCallback< Object >() { @Override public Object doInTransaction() { Iterator<Attachment> i = attachmentManager.getAttachmentDao().findLatestVersionsIterator(); while (i.hasNext()) { Attachment att = i.next(); if (att.getVersion() > 1 && att.getSpace() != null ) { List<Attachment> prior = attachmentManager.getPreviousVersions(a); // remove newest two versions from prior. for (Attachment p : toDelete) { attachmentManager.removeAttachmentVersionFromServer(p); } } } } }); } So what worked in and up to 5.6 is now screwed on me. Any ideas on what I now need to do?

            You are right jens@k15t.com. Sorry for the confusion and thank you for the correction!

            I've fixed the documentation now. See https://developer.atlassian.com/pages/diffpagesbyversion.action?pageId=2031706&selectedPageVersions=27&selectedPageVersions=28.

            Paul Curren added a comment - You are right jens@k15t.com . Sorry for the confusion and thank you for the correction! I've fixed the documentation now. See https://developer.atlassian.com/pages/diffpagesbyversion.action?pageId=2031706&selectedPageVersions=27&selectedPageVersions=28 .

            pcurren:
            The example you've added to the documentation page seems to demonstrate transactions with the Hibernate TransactionTemplate which is not available to plugins AFAIK.
            Shouldn't the example rather demonstrate how to use SAL's TransactionTemplate in this context?

            Jens Rutschmann [K15t] added a comment - pcurren : The example you've added to the documentation page seems to demonstrate transactions with the Hibernate TransactionTemplate which is not available to plugins AFAIK. Shouldn't the example rather demonstrate how to use SAL's TransactionTemplate in this context?

            Paul Curren added a comment - - edited

            The problem is with a long transaction monopolising the database connection. There is also a vaguely related problem you need to watch out for which is related to the Hibernate session growing too large. There are some notes around this.

            Strategies for not 'hogging' the transaction could involve batching the work that is being done, or somehow separating the action of 'finding the data to operate on' and then operating on it. It really depends on what suits your use case best.

            And as to what would be considered a 'long' job. I suppose longer than 2 or 3 seconds is probably 'long'.

            Paul Curren added a comment - - edited The problem is with a long transaction monopolising the database connection. There is also a vaguely related problem you need to watch out for which is related to the Hibernate session growing too large. There are some notes around this. Strategies for not 'hogging' the transaction could involve batching the work that is being done, or somehow separating the action of 'finding the data to operate on' and then operating on it. It really depends on what suits your use case best. And as to what would be considered a 'long' job. I suppose longer than 2 or 3 seconds is probably 'long'.

            Thanks Paul, good work. What is a "long lived job"? How 'long' ? Our page expiry job may run for a while. Which impact does that have on say the pageManager service?

            Ulrich Kuhnhardt [Comalatech] added a comment - Thanks Paul, good work. What is a "long lived job"? How 'long' ? Our page expiry job may run for a while. Which impact does that have on say the pageManager service?

            ulrich1, I have updated the documentation to be clearer while we look at the right thing to do here.

            Paul Curren added a comment - ulrich1 , I have updated the documentation to be clearer while we look at the right thing to do here.

            Note that this issue duplicates the recently closed CONF-21435 which has been known since December 2010. This is a source of long standing confusion.

            Paul Curren added a comment - Note that this issue duplicates the recently closed CONF-21435 which has been known since December 2010. This is a source of long standing confusion.

            Hi Paul,

            thanks for looking into this: I think it's a similar problem to https://jira.atlassian.com/browse/CONF-21435. This comment here suggests wrapping the use of pageManager into a transaction context.

            Inside the job we get the CEO from a lucene index lookup. Could it be that in the past (pre 5.7) somehow a hibernate session had been created? We used to be able to access all data of the CEO without a hibernate session. ...

            Now we need to wrap access to the CEO into a transaction and re-read the CEO with the pageManager

                        final List<AbstractPage> expiredPages = workflowIndexer.getExpiredPages(now);
                        if (log.isInfoEnabled()) {
                            log.info("checking expired states (" + expiredPages.size() + " pages found)");
                        }
                        txTemplate.execute(new TransactionCallback<Void>() {
                            @Override
                            public Void doInTransaction() {
                                for (AbstractPage expiredPage : expiredPages) {
                                    processExpiredPage((AbstractPage)pageManager.getById(expiredPage.getId()), now);
                                }
                                return null;
                            }
                        });
            
            
            

            Ulrich Kuhnhardt [Comalatech] added a comment - Hi Paul, thanks for looking into this: I think it's a similar problem to https://jira.atlassian.com/browse/CONF-21435 . This comment here suggests wrapping the use of pageManager into a transaction context. Inside the job we get the CEO from a lucene index lookup. Could it be that in the past (pre 5.7) somehow a hibernate session had been created? We used to be able to access all data of the CEO without a hibernate session. ... Now we need to wrap access to the CEO into a transaction and re-read the CEO with the pageManager final List<AbstractPage> expiredPages = workflowIndexer.getExpiredPages(now); if (log.isInfoEnabled()) { log.info( "checking expired states (" + expiredPages.size() + " pages found)" ); } txTemplate.execute( new TransactionCallback< Void >() { @Override public Void doInTransaction() { for (AbstractPage expiredPage : expiredPages) { processExpiredPage((AbstractPage)pageManager.getById(expiredPage.getId()), now); } return null ; } });

              Unassigned Unassigned
              10058912f876 Ulrich Kuhnhardt [Comalatech]
              Affected customers:
              4 This affects my team
              Watchers:
              9 Start watching this issue

                Created:
                Updated:
                Resolved: