Uploaded image for project: 'Jira Data Center'
  1. Jira Data Center
  2. JRASERVER-79124

Slow webhook payloads due to N+1 queries when serializing changelog items

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: Medium Medium
    • None
    • 9.12.0, 10.3.0, 10.3.12, 11.0.0, 11.1.1
    • Webhooks
    • None

      Issue Summary

      When Jira serializes issue payloads for webhooks, the process of including changelog items suffers from an N+1 query problem.

      For issues with a large number of changelog entries, Jira fetches metadata for each changelog in a separate database query, sometimes with additional queries for permission checks, resulting in significant performance degradation.

      Steps to Reproduce

      1. Create an issue and perform a large number of updates to generate a substantial changelog history.
      2. Trigger a webhook (e.g., issue updated).
      3. Observe the time taken to generate and deliver the webhook payload.

      Expected Results

      Webhook payloads are generated efficiently, with changelog items fetched in a performant, batched manner.

      Actual Results

      Webhook payload generation is slow. Profiling shows that fetching changelog metadata dominates serialization time due to N+1 queries, for example:

      // ...
      com.atlassian.jira.entity.property.BaseEntityPropertyService.getProperty(BaseEntityPropertyService.java:197)
      com.atlassian.jira.entity.property.BaseEntityPropertyService.getProperty(BaseEntityPropertyService.java:158)
      com.atlassian.jira.issue.changehistory.metadata.DefaultHistoryMetadataManager.getHistoryMetadata(DefaultHistoryMetadataManager.java:50)
      jdk.internal.reflect.GeneratedMethodAccessor1828.invoke(Unknown Source)
      jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(java.base@17.0.16/DelegatingMethodAccessorImpl.java:43)
      java.lang.reflect.Method.invoke(java.base@17.0.16/Method.java:569)
      com.atlassian.plugin.util.ContextClassLoaderSettingInvocationHandler.invoke(ContextClassLoaderSettingInvocationHandler.java:26)
      jdk.proxy3.$Proxy720.getHistoryMetadata(jdk.proxy3/Unknown Source)
      jdk.internal.reflect.GeneratedMethodAccessor1828.invoke(Unknown Source)
      jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(java.base@17.0.16/DelegatingMethodAccessorImpl.java:43)
      java.lang.reflect.Method.invoke(java.base@17.0.16/Method.java:569)
      org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
      org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.doInvoke(ServiceInvoker.java:56)
      org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.invoke(ServiceInvoker.java:60)
      org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
      org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137)
      org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124)
      org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
      org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invokeUnprivileged(ServiceTCCLInterceptor.java:70)
      org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invoke(ServiceTCCLInterceptor.java:53)
      org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
      org.eclipse.gemini.blueprint.service.importer.support.LocalBundleContextAdvice.invoke(LocalBundleContextAdvice.java:57)
      org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
      org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137)
      org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124)
      org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
      org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:241)
      jdk.proxy170.$Proxy3257.getHistoryMetadata(jdk.proxy170/Unknown Source)
      com.atlassian.jira.rest.v2.issue.builder.ChangelogBeanBuilder.makeChangeHistoryBean(ChangelogBeanBuilder.java:57)
      com.atlassian.jira.rest.v2.issue.builder.ChangelogBeanBuilder.build(ChangelogBeanBuilder.java:69)
      com.atlassian.jira.plugins.webhooks.serializer.issue.IssueEventSerializer.getChangelog(IssueEventSerializer.java:79)
      com.atlassian.jira.plugins.webhooks.serializer.issue.IssueEventSerializer.putFields(IssueEventSerializer.java:60)
      com.atlassian.jira.plugins.webhooks.serializer.issue.IssueEventSerializer.putFields(IssueEventSerializer.java:27)
      com.atlassian.jira.plugins.webhooks.serializer.AbstractJiraEventSerializer.build(AbstractJiraEventSerializer.java:24)
      

      Workaround

      Currently there is no known workaround for this behavior. A workaround will be added here when available.

              Unassigned Unassigned
              drauf Daniel Rauf
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Created:
                Updated: