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.