Answering my question,

The problem appeared because I was updating two instances of class Job
simultaneously (I had two tasks in the same queue with bucket size >
1).

@Entity
public class Job {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Key key;

Class Job is a child class of class Schedule:

@Entity
public class Schedule {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;

        @Column(nullable=false)
        private String name;

        @Basic
        private List<Job> jobs;


Seems I can't update such an entities simultaneously, because they
have the same parent Schedule, i.e. job1.getKey().getParent().getId()
== job2.getKey().getParent().getId()

So the workaround was just wait some time so that other transaction
commited:

    private void internalUpdateJob(Job job) {
        try {
            jobDAO.update(job);
        } catch (RollbackException e) {
            //  This may happen if another job from the same schedule
            //  updating at the same time simultaneously

            logger.debug("Retrying update for job: {}", job.getKey());

            //  Give another job a chance to commit, and commit
current job after some delay
            internalUpdateJobAfterDelay(job);
        }
    }

    private void internalUpdateJobAfterDelay(Job job) {
        try {
            logger.debug("Waiting for another job to commit");

            Thread.sleep(1000);

            try {
                //  Transaction will be reopened inside DAO if
required
                jobDAO.update(job);
                logger.debug("Update after delay succeeded");
            } catch (RollbackException e2) {
                logger.error("Update after delay failed", e2);
            }
        } catch (InterruptedException e2) {
            logger.error("Interrupted", e2);
        }
    }


and updated DAO:

        public void update(Job job) {
            if (!em.getTransaction().isActive()){
                // see Application#internalUpdateJob(Job)
                logger.debug("Transaction is not active. Begin new one...");

                // XXX Rewrite this to handle transactions more gracefully
                em.getTransaction().begin();
            }
            em.merge(job);
        }


On Mar 24, 1:24 pm, dmitrygusev <[email protected]> wrote:
> Hi,
>
> In my application I have an exception (see stacktrace below) which
> occurs in the same place but not every time (90% it works without any
> problems).
>
> What could be the reason of this?
>
> Here's what I'm doing (this code gets invoked smultaniously using Task
> Queue API on rate 6/m bucket size 5) with different job key parameter:
>
> // This is where everything starts from
>
> public void onActivate() {
> String jobKey = request.getParameter("key");
>
> logger.debug("Running job: " + stringToKey(jobKey).toString());
>
> try {
> Job job = jobDAO.find(stringToKey(jobKey));
>
> if (job != null) {
> application.runJob(job);}
> } catch (Exception e) {
>
> // Prevent to run job once again on failure
> logger.warn("Error running job", e);
>
> }
> }
>
> ...
>
> public void runJob(Job job) {
> try {
>   // ... doing some work here
>
> jobDAO.update(job);
> jobResultDAO.persistResult(jobResult);} catch (Exception e) {
>
> logger.error("Error executing job " + job.getKey(), e);
>
> }
> }
>
> I'm using Tynamo JPA to manage transactions and here's how I
> configured transaction advisor:
>
>     @Match("*DAO")
>     public static void adviseTransactions(JPATransactionAdvisor
> advisor, MethodAdviceReceiver receiver)   {
>         advisor.addTransactionCommitAdvice(receiver);
>     }
>
> Here's my DAO:
>
> public interface JobDAO {
>         @CommitAfter
>         public abstract List<Job> getJobsByCronString(String cronString);
>         @CommitAfter
>         public abstract void delete(Long scheduleId, Long jobId);
>         @CommitAfter
>         public abstract void update(Job job);
>         @CommitAfter
>         public abstract Job find(Long scheduleId, Long jobId);
>         @CommitAfter
>         public abstract Job find(Key stringToKey);
>
> }
>
> public class JobDAOImpl implements JobDAO {
>
>         @Inject
>     public EntityManager em;
>
>         public List<Job> getJobsByCronString(String cronString) {
>                 Query q = em.createQuery("SELECT j FROM Job j WHERE 
> j.cronString
> = :cronString");
>                 q.setParameter("cronString", cronString);
>                 return q.getResultList();
>         }
>
>         public void update(Job job) {
>                 em.merge(job);
>         }
>
>         public void delete(Long scheduleId, Long id) {
>                 Job job = find(scheduleId, id);
>
>                 if (job != null) {
>                         em.remove(job);
>                 }
>         }
>
>         @Override
>         public Job find(Long scheduleId, Long id) {
>                 Query q = em.createQuery("SELECT j FROM Job j WHERE j.key = 
> :key").
>                         setParameter("key", 
> createKey(Schedule.class.getSimpleName(),
> scheduleId).
>                                         getChild(Job.class.getSimpleName(), 
> id));
>
>                 List<Job> result = q.getResultList();
>
>                 return result.isEmpty() ? null : result.get(0);
>         }
>
>         @Override
>         public Job find(Key jobKey) {
>                 return em.find(Job.class, jobKey);
>         }
>
> }
>
> 03-24 02:10AM 16.192
>
> org.datanucleus.transaction.Transaction commit: Operation commit
> failed on resource:
> org.datanucleus.store.appengine.datastorexaresou...@144ffa4, error
> code UNKNOWN and transaction: [DataNucleus Transaction, ID=Xid=
>
> #
> E 03-24 02:10AM 16.193
>
> dmitrygusev.ping.services.Application runJob: Error executing job
> Schedule(662711)/Job(1)
> javax.persistence.RollbackException: Transaction failed to commit
>         at
> org.datanucleus.jpa.EntityTransactionImpl.commit(EntityTransactionImpl.java:
> 118)
>         at
> org.datanucleus.store.appengine.jpa.DatastoreEntityTransactionImpl.commit(DatastoreEntityTransactionImpl.java:
> 50)
>         at
> org.tynamo.jpa.internal.JPATransactionManagerImpl.commit(JPATransactionManagerImpl.java:
> 53)
>         at
> $JPATransactionManager_1278f6494f7.commit($JPATransactionManager_1278f6494f7.java)
>         at
> $JPATransactionManager_1278f6494a9.commit($JPATransactionManager_1278f6494a9.java)
>         at org.tynamo.jpa.internal.JPATransactionAdvisorImpl
> $1.advise(JPATransactionAdvisorImpl.java:46)
>         at
> org.apache.tapestry5.ioc.internal.services.AbstractInvocation.proceed(AbstractInvocation.java:
> 128)
>         at $JobDAO_1278f6494f5.update($JobDAO_1278f6494f5.java)
>         at $JobDAO_1278f6494d9.update($JobDAO_1278f6494d9.java)
>         atdmitrygusev.ping.services.Application.runJob(Application.java:409)

-- 
You received this message because you are subscribed to the Google Groups 
"Google App Engine for Java" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/google-appengine-java?hl=en.

Reply via email to