Dajbych.net


Problémy a příčiny úniků paměti v Service Fabric Reliable Services

, 2 minuty čtení

service fabric logo

Úniky paměti se těžko odhalují a mohou způsobit vážné problémy. Zatímco jedna úloha běžící několik sekund se o ně nemusí starat, služba běžící 24 hodin denně musí být pečlivě vyladěna, aby svou práci úspěšně plnila. Úniky paměti navíc nemusí být detekovány okamžitě; Obvykle se zobrazují jako další výjimka, která vás upozorní na problémy v jiné oblasti. Podívejme se na jeden příklad úniku paměti a na to, jak se projevil.

Dobrá služba Service Fabric šíří CancellationToken na každé možné očekávané volání (proč? přečtěte si můj předchozí článek). S ohledem na to jsem důsledně psal kód takto:

using (var conn = new SqlConnection("...")) {
    await conn.OpenAsync(cancellationToken).ConfigureAwait(false);
    using (var cmd = new SqlCommand("...", conn)) {
        ...
        using (var reader = cmd.ExecuteReaderAsync(cancellationToken)) {
            while (await reader.ReadAsync(cancellationToken)) {
                ...
            }
        }
    }
}

Kód vypadá dobře, že? Using prohlášení o všem IDisposable, zavedené ADO.NET technologii s dlouhou historií sahající až do srpna 1996. Myslel jsem si, že se zde nemůže vůbec nic pokazit, ale mýlil jsem se. Výše uvedený kód obsahuje únik paměti. Není to vidět a nehází to OutOfMemoryException.

Zachycená výjimka byla jiná. Jednalo se o Win32Exception s tvrzením:

A connection was successfully established with the server, but then an error occurred during the pre-login handshake.

Navíc rostoucí výjimky nebyly konzistentní. Někdy chybová zpráva vypadala takto:

The client was unable to establish a connection because of an error during the connection initialization process before login. Possible causes include the following: the client tried to connect to an unsupported version of SQL Server; the server was too busy to accept new connections; or there was a resource limitation (insufficient memory or maximum allowed connections) on the server.

Tato zpráva byla velmi užitečná, protože nepodporovaná verze SQL Server je u SQL Database v Azure velmi nepravděpodobná a Query Performance Insights zobrazuje problémy s výkonem přesněji než výjimky v Application Insights. Cluster Service Fabric navíc hlásil něco jiného:

SourceId='System.FabricNode', Property='SecurityApi_CertGetCertificateChain', HealthState='Warning', ConsiderWarningAsError=false, …

Porovnal jsem skutečný kód s nejnovější stabilní verzí a našel jsem problém. Jedná se o metodu SqlDataReader.ReadAsync, která obsahuje únik paměti. Základní chyba v této metodě byla hlášena již před mým vyšetřováním, takže moje práce byla mnohem jednodušší. Je opraven v rozhraní .NET 4.7, které je obecně dostupné v květnu 2017. Nemohu říci, zda tato chyba v tuto chvíli způsobuje i únik paměti. Právě jsem se vrátil k synchronnímu hovoru. Je to mnohem jednodušší než vynucovat základní škálovací sadu virtuálních strojů se systémem Windows Server 2016, aby nainstalovala nejnovější rozhraní .NET Framework (a znovu po každém možném obnovení clusteru).

Službu Service Fabric je možné aktualizovat v monitorovaném režimu. Může se vrátit zpět, když nová verze selže pouze kvůli chybám při zpracování velkých dat – typicky únikům paměti. Služba není v pořádku nemusí nutně znamenat, že služba vyvolává nějaké výjimky. Znamená to také, že nezpracovává tolik dat, kolik se očekávalo – obvykle kvůli výměně paměti.