Quartznet: Fehler: Quartz.NET löst unbehandelte Ausnahme aus, wenn .net Core 3.1-Debugging-Flags aktiviert sind

Erstellt am 5. Mai 2021  ·  6Kommentare  ·  Quelle: quartznet/quartznet

Beschreibe den Fehler

Ich verwende hier .net Core 3.1 und Quartz 3.3.2 und habe leider ein Problem, bei dem ich hoffe, dass Sie mir helfen können:

Auf einer hohen Ebene löst meine dockerisierte asp.net-Anwendung immer eine unbehandelte Ausnahme aus, wenn sie zum ersten Mal die Methode execute meiner einzigen Impl-of-IJob-Schnittstelle (bisher) aufruft. Bei lokaler Ausführung tritt die Ausnahme nicht auf.

Ich habe das Tutorial von Andrew Lock hier befolgt:
https://andrelock.net/creating-a-quartz-net-hosted-service-with-asp-net-core/

Mein program.cs Hostbuilder sieht jetzt so aus:

...
Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
                .ConfigureLogging((hostingContext, logging) =>
                {
                    ...
                })
                .UseNLog()
                .ConfigureServices(
                    (hostContext, services) =>
                    {
                        services.AddHostedService<OtherHostedServiceWithoutQuartz>();

                        // Add the required Quartz.NET services
                        services.AddQuartz(quartzConfig =>
                        {
                            quartzConfig.SchedulerId = "my-scheduler";
                            quartzConfig.SchedulerName = "MyScheduler";
                            quartzConfig.UseDefaultThreadPool(options => options.MaxConcurrency = 3);

                            // tells Quartz.NET to register an IJobFactory that creates jobs for transient, scoped or singleton services by fetching them from the DI container.
                            quartzConfigurator.AddJobAndTrigger<MyJob>(
                                hostContext.Configuration);
                        });

                        // Add the Quartz.NET hosted service and allow graceful shutdown.
                        services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
                    });

Ich erhalte also das folgende Ausnahmeprotokoll, wenn ich meinen Dienst im Docker starte:

2021-05-05 09:11:24.2691 | DEBUG | Calling Execute on job DEFAULT.MyJob 
2021-05-05 09:11:24.2737 | ERROR | Job DEFAULT.MyJob threw an unhandled Exception:  System.InvalidOperationException: Not a recovering job
   at Quartz.Impl.JobExecutionContextImpl.get_RecoveringTriggerKey()
   at System.Diagnostics.DiagnosticSourceEventSource.TransformSpec.PropertySpec.PropertyFetch.RefTypedFetchProperty`2.Fetch(Object obj)
   at System.Diagnostics.DiagnosticSourceEventSource.TransformSpec.PropertySpec.Fetch(Object obj)
   at System.Diagnostics.DiagnosticSourceEventSource.TransformSpec.Morph(Object obj)
   at System.Diagnostics.DiagnosticSourceEventSource.FilterAndTransform.Morph(Object args)
   at System.Diagnostics.DiagnosticSourceEventSource.FilterAndTransform.<>c__DisplayClass2_1.<.ctor>b__2(KeyValuePair`2 evnt)
   at System.Diagnostics.DiagnosticSourceEventSource.CallbackObserver`1.OnNext(T value)
   at System.Diagnostics.DiagnosticListener.Write(String name, Object value)
   at System.Diagnostics.DiagnosticSource.StartActivity(Activity activity, Object args)
   at Quartz.Logging.JobDiagnosticsWriter.WriteStarted(IJobExecutionContext context, DateTimeOffset startTimeUtc)
   at Quartz.Core.JobRunShell.Run(CancellationToken cancellationToken)
2021-05-05 09:11:24.3217 | ERROR | Job DEFAULT.MyJob threw an exception. Quartz.SchedulerException: Job threw an unhandled exception.
 ---> System.InvalidOperationException: Not a recovering job
   at Quartz.Impl.JobExecutionContextImpl.get_RecoveringTriggerKey()
   at System.Diagnostics.DiagnosticSourceEventSource.TransformSpec.PropertySpec.PropertyFetch.RefTypedFetchProperty`2.Fetch(Object obj)
   at System.Diagnostics.DiagnosticSourceEventSource.TransformSpec.PropertySpec.Fetch(Object obj)
   at System.Diagnostics.DiagnosticSourceEventSource.TransformSpec.Morph(Object obj)
   at System.Diagnostics.DiagnosticSourceEventSource.FilterAndTransform.Morph(Object args)
   at System.Diagnostics.DiagnosticSourceEventSource.FilterAndTransform.<>c__DisplayClass2_1.<.ctor>b__2(KeyValuePair`2 evnt)
   at System.Diagnostics.DiagnosticSourceEventSource.CallbackObserver`1.OnNext(T value)
   at System.Diagnostics.DiagnosticListener.Write(String name, Object value)
   at System.Diagnostics.DiagnosticSource.StartActivity(Activity activity, Object args)
   at Quartz.Logging.JobDiagnosticsWriter.WriteStarted(IJobExecutionContext context, DateTimeOffset startTimeUtc)
   at Quartz.Core.JobRunShell.Run(CancellationToken cancellationToken)
   --- End of inner exception stack trace --- [See nested exception: System.InvalidOperationException: Not a recovering job
   at Quartz.Impl.JobExecutionContextImpl.get_RecoveringTriggerKey()
   at System.Diagnostics.DiagnosticSourceEventSource.TransformSpec.PropertySpec.PropertyFetch.RefTypedFetchProperty`2.Fetch(Object obj)
   at System.Diagnostics.DiagnosticSourceEventSource.TransformSpec.PropertySpec.Fetch(Object obj)
   at System.Diagnostics.DiagnosticSourceEventSource.TransformSpec.Morph(Object obj)
   at System.Diagnostics.DiagnosticSourceEventSource.FilterAndTransform.Morph(Object args)
   at System.Diagnostics.DiagnosticSourceEventSource.FilterAndTransform.<>c__DisplayClass2_1.<.ctor>b__2(KeyValuePair`2 evnt)
   at System.Diagnostics.DiagnosticSourceEventSource.CallbackObserver`1.OnNext(T value)
   at System.Diagnostics.DiagnosticListener.Write(String name, Object value)
   at System.Diagnostics.DiagnosticSource.StartActivity(Activity activity, Object args)
   at Quartz.Logging.JobDiagnosticsWriter.WriteStarted(IJobExecutionContext context, DateTimeOffset startTimeUtc)
   at Quartz.Core.JobRunShell.Run(CancellationToken cancellationToken)]
System.InvalidOperationException: Not a recovering job
   at Quartz.Impl.JobExecutionContextImpl.get_RecoveringTriggerKey()
   at System.Diagnostics.DiagnosticSourceEventSource.TransformSpec.PropertySpec.PropertyFetch.RefTypedFetchProperty`2.Fetch(Object obj)
   at System.Diagnostics.DiagnosticSourceEventSource.TransformSpec.PropertySpec.Fetch(Object obj)
   at System.Diagnostics.DiagnosticSourceEventSource.TransformSpec.Morph(Object obj)
   at System.Diagnostics.DiagnosticSourceEventSource.FilterAndTransform.Morph(Object args)
   at System.Diagnostics.DiagnosticSourceEventSource.FilterAndTransform.<>c__DisplayClass2_1.<.ctor>b__2(KeyValuePair`2 evnt)
   at System.Diagnostics.DiagnosticSourceEventSource.CallbackObserver`1.OnNext(T value)
   at System.Diagnostics.DiagnosticListener.Write(String name, Object value)
   at System.Diagnostics.DiagnosticSource.StartActivity(Activity activity, Object args)
   at Quartz.Logging.JobDiagnosticsWriter.WriteStarted(IJobExecutionContext context, DateTimeOffset startTimeUtc)
   at Quartz.Core.JobRunShell.Run(CancellationToken cancellationToken)
2021-05-05 09:11:24.3296 | DEBUG | Trigger instruction : NoInstruction 
2021-05-05 09:11:24.3361 | DEBUG | Batch acquisition of 1 triggers 

Ich habe die folgende Ausführungsmethode in meiner MyJob.cs, wobei für MyJob [DisallowConcurrentExecution] gesetzt ist:

...
/// <summary>
        /// using quartz for periodic publishing.
        /// </summary>
        /// <param name="context">quartz' execution context.</param>
        public async Task Execute(IJobExecutionContext context)
        {
            try
            {
                _logger.LogDebug($"Execution of Periodic Task in {nameof(MyJob)}...");
                await Publish();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Execution of Periodic Task in {nameof(MyJob)} failed!");

                // false => don't refire the event immediately
                throw new JobExecutionException("Exception in periodic Handling, skipping Tick.", ex, false);
            }
        }

Um es kurz zu machen: Ich habe keine Ahnung, warum mein Ausnahmehandler die auftretende Ausnahme beim Aufrufen der Execute-Methode nicht behandelt. Es fühlt sich an, als ob diese Ausnahme ein zugrunde liegendes Problem verbirgt, aber ich kann es nicht debuggen, da ich lokal kein Problem habe, Execute wird nur alle x Sekunden ausgeführt. Oder dass etwas im IJobExecutionContext die unbehandelte Ausnahme auslöst. Tatsächlich führt dies nicht zum Herunterfahren meines Hostedservice, die Ausnahme wird nur in jedem Intervall geworfen.

Jede Hilfe, warum diese Ausnahme nicht behandelt wird, wäre großartig!

Ehrlich gesagt denke ich, dass ich hier etwas falsch mache oder kein Konzept bekomme ;)

Mit freundlichen Grüßen,
Dominik
Verwendete Version

QUARZ .net 3.3.2
Docker 19.03.6

Erwartetes Verhalten
1) Führen Sie Brände normal ohne unbehandelte Ausnahme aus.
2) Wenn eine nicht behandelte Ausnahme ausgelöst wird, wird sie von try/catch in execute behandelt.

Zusätzlicher Kontext
Fügen Sie hier jeden weiteren Kontext zu dem Problem hinzu.

Alle 6 Kommentare

Danke für den ausführlichen Bericht. Scheint, dass dies ein Fehler beim Schreiben von Traces ist, Trace Writer schreibt den Kontext und es gibt eine nicht so schöne Eigenschaft, die getter aufrufen kann, wenn der Kontext nicht in dem Zustand ist, in dem der Aufruf erwartet wird. Bis dies ordnungsgemäß behoben ist, sollte es meiner Meinung nach eine Möglichkeit geben, den Trace-Kontext für Quartz zu deaktivieren, wenn Sie ihn irgendwie für die Verwendung konfiguriert haben (Aktivitäten, Überwachung, möglicherweise Protokollierung).

Der Diagnose-Listener hat den Namen "Quartz".

Danke für die schnelle Antwort. Ich bin ziemlich neu bei Quartz.NET und Quartz im Allgemeinen. Könnten Sie bitte etwas mehr zum Deaktivieren des Ablaufverfolgungskontexts / des Diagnoselisteners ausführen? Ich habe wirklich nicht mehr Konfiguration als gezeigt, abgesehen von der Komfortmethode AddJobAndTrigger , die dieselbe ist wie in Andrews-Artikel. Vielen Dank im Voraus! :)

Es tut mir leid, aber ich werde frühestens Zeit haben, tiefer in diesen Abend einzutauchen (ohne Gewähr!), aber Sie könnten versuchen, andere Logger zumindest in Ihrem ConfigureLogging zu löschen:

ConfigureLogging(x => x.ClearProviders());

Danach sollte UseNLog nur NLog registrieren...

Das mache ich tatsächlich gerade. Hier ist der vollständige Code meiner BuildWebHost-Methode:

public static IHostBuilder BuildWebHost(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
                .ConfigureLogging((hostingContext, logging) =>
                {
                    logging.ClearProviders();
                    logging.SetMinimumLevel(LogLevel.Trace);
                })
                .UseNLog()
                .ConfigureServices(
                    (hostContext, services) =>
                    {
                        services.AddHostedService<LifecycleOrchestrator>();

                        // Add the required Quartz.NET services
                        services.AddQuartz(quartzConfigurator =>
                        {
                            quartzConfigurator.SchedulerId = "my-scheduler";
                            quartzConfigurator.SchedulerName = "MyScheduler";
                            quartzConfigurator.UseDefaultThreadPool(options => options.MaxConcurrency = 3);

                            // tells Quartz.NET to register an IJobFactory that creates jobs for transient, scoped or singleton services
                            // by fetching them from the DI container.
                            quartzConfigurator.UseMicrosoftDependencyInjectionScopedJobFactory();
                            quartzConfigurator.AddJobAndTrigger<MyJob>(
                                hostContext.Configuration);
                        });

                        // Add the Quartz.NET hosted service
                        services.AddQuartzHostedService(
                            quartzOptions => quartzOptions.WaitForJobsToComplete = true);
                    });

das löst das Problem leider nicht. Ich werde aber nachsehen, ob ich es selbst finde :) Nochmals vielen Dank!

@lahma

Ok, ich habe tiefer gegraben und konnte es festnageln.

Kurzer Kontext: Ich habe zwei Pipelines, beide mit Bereitstellung auf demselben Docker-Host. Eine Pipeline-Bereitstellung hat funktioniert, die andere nicht. Also habe ich einige Diffs meiner ausgeführten Docker-Container-Docker-Inspect-Ausgabe gemacht, und um es kurz zu machen:

Solange die folgenden env vars für meinen Container nicht gesetzt sind, funktioniert Quartz einwandfrei:

$(echo --env COMPlus_PerfMapEnabled=1)
$(echo --env COMPlus_EnableEventLog=1)

Weitere Informationen finden Sie in den MS-Dokumenten (zB hier): https://docs.microsoft.com/en-us/dotnet/core/run-time-config/debugging-profiling

In der einen Pipeline sind diese gesetzt, in der anderen nicht.

Danach habe ich es lokal ohne Docker versucht, indem ich nur setze
```
export COMPlus_EnableEventLog=1
````
gefolgt von einem dotnet-Lauf meines Projekts, und was soll ich sagen: Ich konnte den Fehler jetzt auch lokal reproduzieren. Dies ist also höchstwahrscheinlich ein Fehler, wenn Quartz.NET verwendet wird und die Debug-Flags von .net Core aktiviert sind. :)

Solange es keinen Bugfix gibt, werde ich die env vars jetzt aus meiner Pipeline für diesen dedizierten Container entfernen, ich brauche sie nicht wirklich, sie werden nur für Debugging-Zwecke für jede containerisierte Lösung in der Testumgebung festgelegt. Aber ich hoffe, das kann dir helfen, die Lösung zu finden :)

Bearbeiten: Wenn Sie möchten, kann ich ein anderes Problem für dieses spezifischere und reproduzierbare Problem erstellen. ;)

Schön zu hören, dass Sie einen Workaround gefunden haben. Danke für die Beschreibung des Setups, sollte bei der Fehlerbehebung helfen!

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen