Quartznet: Pic de CPU de 40 % dĂ» Ă  une requĂȘte sur QRTZ_LOCKS

CrĂ©Ă© le 8 fĂ©vr. 2021  Â·  14Commentaires  Â·  Source: quartznet/quartznet

Mon application s'exĂ©cute sur .NET Framework 4.7.2 et exĂ©cutait une ancienne version 3.0.7 de Quartz depuis plus d'un an avec une faible utilisation du processeur, et il y a quelques semaines, nous avons mis Ă  niveau Quartz vers 3.2.3 et nous avons remarquĂ© une augmentation immĂ©diate de 40 % du processeur. car cette requĂȘte est exĂ©cutĂ©e beaucoup plus souvent avec la nouvelle version.

SÉLECTIONNER *
DE QRTZ_LOCKS AVEC (UPDLOCK, ROWLOCK)
WHERE SCHED_NAME = @schedulerName
ET LOCK_NAME = @lockName

Version utilisée

Version : 3.2.3

Reproduire

Je n'ai pas de code à reproduire, mais mon application crée des tùches simples et à un moment donné, elle en a 10 exécutées sur 4 instances de VM différentes.

Comportement attendu

Le processeur doit rester tel qu'il Ă©tait avec l'ancienne version.

btw, il n'y a pas d'erreurs signalées par Quartz ou notre application, mais c'est le seul effet secondaire que nous voyons avec la version 3.2.3

Tous les 14 commentaires

Pouvez-vous publier la configuration d'usine de votre planificateur (suppression des informations d'identification, etc.) ?

Sûr.

==== processeurOrdonnanceur ====

<scheduler name="processorScheduler">
<quartz>
  <property key="quartz.scheduler.instanceName" value="ProcessorScheduler" />
  <property key="quartz.scheduler.instanceId" value="AUTO" />
  <property key="quartz.scheduler.idleWaitTime" value="1000" />
  <property key="quartz.scheduler.exporter.type" value="Quartz.Simpl.RemotingSchedulerExporter, Quartz" />
  <property key="quartz.scheduler.exporter.port" value="1111" />
  <property key="quartz.scheduler.exporter.bindName" value="ProcessorScheduler" />
  <property key="quartz.scheduler.exporter.channelType" value="tcp" />
  <property key="quartz.scheduler.exporter.channelName" value="httpQuartz" />
  <property key="quartz.threadPool.type" value="Quartz.Simpl.DefaultThreadPool, Quartz" />
  <property key="quartz.threadPool.threadCount" value="20" />
  <property key="quartz.jobStore.type" value="Quartz.Impl.AdoJobStore.JobStoreTX, Quartz" />
  <property key="quartz.serializer.type" value="binary" />
  <property key="quartz.jobStore.clustered" value="true" />
  <property key="quartz.jobStore.clusterCheckinInterval" value="1000" />
  <property key="quartz.jobStore.misfireThreshold" value="60000" />
  <property key="quartz.jobStore.dataSource" value="default" />
  <property key="quartz.jobStore.driverDelegateType" value="Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz" />
  <property key="quartz.jobStore.tablePrefix" value="QRTZ_" />
  <property key="quartz.jobStore.useProperties" value="true" />
  <property key="quartz.dataSource.default.connectionString" value="Server=xxx;Database=quartz;user id=xxx;PWD=xxx;" />
  <property key="quartz.dataSource.default.provider" value="SqlServer" />
</quartz>
</scheduler>
<scheduler name="notificationScheduler">
<quartz>
  <property key="quartz.scheduler.instanceName" value="notificationScheduler" />
  <property key="quartz.scheduler.instanceId" value="notificationSchedulerInstance" />
  <property key="quartz.scheduler.proxy" value="true" />
  <property key="quartz.scheduler.proxy.address" value="tcp://xxx:2222/notificationScheduler" />
  <property key="quartz.threadPool.type" value="Quartz.Simpl.DefaultThreadPool, Quartz" />
  <property key="quartz.jobStore.type" value="Quartz.Impl.AdoJobStore.JobStoreTX, Quartz" />
  <property key="quartz.jobStore.misfireThreshold" value="60000" />
  <property key="quartz.jobStore.dataSource" value="default" />
  <property key="quartz.jobStore.driverDelegateType" value="Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz" />
  <property key="quartz.jobStore.lockHandler.type" value="Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz" />
  <property key="quartz.jobStore.tablePrefix" value="QRTZ_" />
  <property key="quartz.jobStore.useProperties" value="true" />
</quartz>
</scheduler>

==== planificateur de notifications ====

<scheduler name="notificationScheduler">
<quartz>
  <property key="quartz.scheduler.instanceName" value="notificationScheduler" />
  <property key="quartz.scheduler.instanceId" value="AUTO" />
  <property key="quartz.scheduler.idleWaitTime" value="1000" />
  <property key="quartz.scheduler.exporter.type" value="Quartz.Simpl.RemotingSchedulerExporter, Quartz" />
  <property key="quartz.scheduler.exporter.port" value="2222" />
  <property key="quartz.scheduler.exporter.bindName" value="notificationScheduler" />
  <property key="quartz.scheduler.exporter.channelType" value="tcp" />
  <property key="quartz.scheduler.exporter.channelName" value="httpQuartz" />
  <property key="quartz.threadPool.type" value="Quartz.Simpl.DefaultThreadPool, Quartz" />
  <property key="quartz.threadPool.threadCount" value="20" />
  <property key="quartz.jobStore.type" value="Quartz.Impl.AdoJobStore.JobStoreTX, Quartz" />
  <property key="quartz.serializer.type" value="binary" />
  <property key="quartz.jobStore.clustered" value="true" />
  <property key="quartz.jobStore.clusterCheckinInterval" value="1000" />
  <property key="quartz.jobStore.misfireThreshold" value="60000" />
  <property key="quartz.jobStore.dataSource" value="default" />
  <property key="quartz.jobStore.driverDelegateType" value="Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz" />
  <property key="quartz.jobStore.tablePrefix" value="QRTZ_" />
  <property key="quartz.jobStore.useProperties" value="true" />
  <property key="quartz.dataSource.default.connectionString" value="Server=xxx;Database=quartz;user id=xxx;PWD=xxx;" />
  <property key="quartz.dataSource.default.provider" value="SqlServer" />
</quartz>
</scheduler>

J'aurai besoin de temps pour enquĂȘter, mais je suppose que le plus grand changement a Ă©tĂ© que la requĂȘte a Ă©tĂ© paramĂ©trĂ©e, ce qui donne Ă©galement l'impression d'ĂȘtre exĂ©cutĂ©e deux fois plus frĂ©quemment si vous avez deux planificateurs distincts (auparavant, il y avait une chaĂźne SQL diffĂ©rente pour chacun de eux).

C'est exact, j'ai plusieurs planificateurs qui ont tous des configurations similaires Ă  celles ci-dessus.
Merci de l'avoir examiné.

Salut Marko, touchant la base pour toute mise à jour ?

J'espĂšre avoir le temps de travailler avec ce week-end. Je n'ai rien trouvĂ© d'Ă©vident provoquant une telle rĂ©gression des performances, mais je pense qu'il y a quelque chose entre 2.x et 3.x qui peut ĂȘtre amĂ©liorĂ©.

Salut Marko, as-tu eu la monnaie de jeter un Ɠil à ce pote ?

DĂ©solĂ©, pas de gros gains pour l'instant. J'en ai discutĂ© avec SQL Server DBA et il n'a trouvĂ© aucune raison Ă©vidente en testant, donc cela ne devrait pas ĂȘtre du cĂŽtĂ© de la base de donnĂ©es car il s'exĂ©cute sur la mĂȘme page de base de donnĂ©es qui devrait ĂȘtre super rapide (petit nombre de lignes, deux colonnes) . Si vous avez le temps de profiler pour identifier quelque chose d'Ă©vident qui me manque, ce serait super.

Malheureusement, nous ne pouvons pas établir de profil. Mais, j'ai une autre idée à partager avec vous.
Avant le jour oĂč nous avons publiĂ© notre application avec Quartz mis Ă  jour, cette requĂȘte Ă©tait exĂ©cutĂ©e 250 000 fois par jour. Juste aprĂšs la sortie de Quartz v3.0.7, les temps d'exĂ©cution des requĂȘtes sont passĂ©s Ă  350 000 fois par jour (un saut de 125 000). Notre trafic de donnĂ©es d'application n'a pas changĂ© du tout.

De plus, nous avons remarquĂ© que votre requĂȘte fait UPDLOCK, ROWLOCK sur une instruction SELECT, une raison pour laquelle elle doit le faire lorsqu'elle n'est pas mise Ă  jour ? Je pense que si vous changez cela en NOLOCK, ce serait mieux.

SÉLECTIONNER *
DE QRTZ_LOCKS AVEC (UPDLOCK, ROWLOCK)
WHERE SCHED_NAME = @schedulerName
ET LOCK_NAME = @lockName

Quelque chose dans la v3.0.7 fait que cette requĂȘte s'exĂ©cute plus souvent.

Je viens de parler Ă  nouveau Ă  mon administrateur de base de donnĂ©es et il a creusĂ© un peu plus et dĂ©couvert dans l'ancienne version que cette requĂȘte UPDATE Ă©tait utilisĂ©e pour ĂȘtre exĂ©cutĂ©e, et depuis la mise Ă  niveau, cette requĂȘte a disparu et le nouveau SELECT ci-dessus est apparu.

Notez Ă©galement que Sched_name sortait sous forme de texte et non de paramĂštre.

( @lockName nvarchar(14))
METTRE À JOUR QRTZ_LOCKS
DÉFINIR LOCK_NAME = LOCK_NAME
WHERE SCHED_NAME = 'MaintenanceScheduler' AND LOCK_NAME = @lockName

Et l'ancienne instruction SELECT Ă©tait celle-ci par rapport Ă  la nouvelle.

Avant le 27 janvier
( @lockName nvarchar(14))
SELECT * FROM QRTZ_LOCKS AVEC (UPDLOCK, ROWLOCK)
WHERE SCHED_NAME = 'Planificateur de maintenance'
ET LOCK_NAME = @lockName

J'espĂšre que cela t'aides.

Merci pour la mise Ă  jour et les informations.

De plus, nous avons remarquĂ© que votre requĂȘte fait UPDLOCK, ROWLOCK sur une instruction SELECT, une raison pour laquelle elle doit le faire lorsqu'elle n'est pas mise Ă  jour ? Je pense que si vous changez cela en NOLOCK, ce serait mieux.

Cela signifierait qu'il n'y aurait pas de verrous pour se protĂ©ger des accĂšs concurrents, donc je n'irais pas lĂ -bas 😉

Notez Ă©galement que Sched_name sortait sous forme de texte et non de paramĂštre.

C'est le comportement exact qui Ă©tait prĂ©vu aprĂšs la fusion de #818. Vous devriez maintenant voir beaucoup plus de requĂȘtes avec ce SQL exact si vous avez plusieurs planificateurs, ils utilisent tous le mĂȘme plan de requĂȘte grĂące Ă  l'utilisation du paramĂštre de requĂȘte au lieu de coder en dur le nom du planificateur dans la requĂȘte, ce qui entraĂźne des plans diffĂ©rents.

Relisez votre configuration notificationScheduler :

<property key="quartz.jobStore.lockHandler.type" value="Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz" />

Cela l'empĂȘchera d'utiliser l'instruction SQL optimisĂ©e destinĂ©e Ă  SQL Server. Voir https://github.com/quartznet/quartznet/blob/f612e3f66ab27e221b5632269ef5c25c0fe8bcc5/src/Quartz/Impl/AdoJobStore/JobStoreSupport.cs#L492 -L521 pour la logique.

J'ai supprimé cette propriété et également mis à niveau vers la derniÚre version, vous permettra de savoir comment il se comporte.

Salut Marko, juste une mise Ă  jour. Le processeur est revenu Ă  un comportement normal aprĂšs les mises Ă  jour ci-dessus.
Merci pour ton aide l'ami.

Super à entendre, merci d'avoir bouclé la boucle.

Cette page vous a été utile?
0 / 5 - 0 notes