Foreign key constraint violation during Audit Event history purge

Problem

If log output for JEMH is enabled, the following (or similar) may be seen in the log output:

2019-02-08 12:45:07,348 JEMH-Housekeeping-Worker-1 ERROR [emh.ui.ao.JEMHActiveObjectServiceImpl] Unable to deleteAuditEventsOlderThan(): There was a SQL exception thrown by the Active Objects library: Database: - name:PostgreSQL - version:9.4.20 - minor version:4 - major version:9 Driver: - name:PostgreSQL Native Driver - version:PostgreSQL 9.4.1212 org.postgresql.util.PSQLException: ERROR: update or delete on table "AO_78C957_AUDITEVENTS" violates foreign key constraint "fk_ao_78c957_audithint_audit_event_id" on table "AO_78C957_AUDITHINT" Detail: Key (ID)=(2) is still referenced from table "AO_78C957_AUDITHINT". com.atlassian.activeobjects.internal.ActiveObjectsSqlException: There was a SQL exception thrown by the Active Objects library: Database: - name:PostgreSQL - version:9.4.20 - minor version:4 - major version:9 Driver: - name:PostgreSQL Native Driver - version:PostgreSQL 9.4.1212 org.postgresql.util.PSQLException: ERROR: update or delete on table "AO_78C957_AUDITEVENTS" violates foreign key constraint "fk_ao_78c957_audithint_audit_event_id" on table "AO_78C957_AUDITHINT" Detail: Key (ID)=(2) is still referenced from table "AO_78C957_AUDITHINT". at com.atlassian.activeobjects.internal.EntityManagedActiveObjects.deleteWithSQL(EntityManagedActiveObjects.java:118) at com.atlassian.activeobjects.osgi.TenantAwareActiveObjects.deleteWithSQL(TenantAwareActiveObjects.java:281) at sun.reflect.GeneratedMethodAccessor938.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.doInvoke(ServiceInvoker.java:56) at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.invoke(ServiceInvoker.java:60) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133) at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invokeUnprivileged(ServiceTCCLInterceptor.java:70) at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invoke(ServiceTCCLInterceptor.java:53) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.eclipse.gemini.blueprint.service.importer.support.LocalBundleContextAdvice.invoke(LocalBundleContextAdvice.java:57) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133) at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) at com.sun.proxy.$Proxy4875.deleteWithSQL(Unknown Source) at com.javahollic.jira.emh.ui.ao.JEMHActiveObjectServiceImpl.deleteAuditEventsOlderThan(JEMHActiveObjectServiceImpl.java:1617) at com.javahollic.jira.emh.service.scheduler.jobs.HousekeepingJob.purgeAuditHistory(HousekeepingJob.java:342) at com.javahollic.jira.emh.service.scheduler.jobs.HousekeepingJob.clearAuditHistory(HousekeepingJob.java:283) at com.javahollic.jira.emh.service.scheduler.jobs.HousekeepingJob.access$200(HousekeepingJob.java:34) at com.javahollic.jira.emh.service.scheduler.jobs.HousekeepingJob$2.run(HousekeepingJob.java:192) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: org.postgresql.util.PSQLException: ERROR: update or delete on table "AO_78C957_AUDITEVENTS" violates foreign key constraint "fk_ao_78c957_audithint_audit_event_id" on table "AO_78C957_AUDITHINT" Detail: Key (ID)=(2) is still referenced from table "AO_78C957_AUDITHINT". at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2455) at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2155) at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:288) at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:430) at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:356) at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:168) at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:135) at org.apache.commons.dbcp2.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:98) at org.apache.commons.dbcp2.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:98) at net.java.ao.EntityManager.deleteWithSQL(EntityManager.java:514) at com.atlassian.activeobjects.internal.EntityManagedActiveObjects.deleteWithSQL(EntityManagedActiveObjects.java:116) ... 31 more

This can occur on rare occasions due to the way the audit hints are created.

Solution