Azure Powershell – nejsou vidět subscriptions

Dnes jsem narazil na situaci, kdy jsem v powershellu neviděl subscriptions, které jsem měl dříve k dispozici. Nepomáhalo ani Add-AzureAccount, ani odebrání (všech) accountů a jejich opětovné přidání.

Jako řešení zafungovalo smazání složky C:\Users\USERNAME\AppData\Roaming\Windows Azure Powershell, ve které jsou standardně uloženy “Subscription Data Files”.

CLR Profiler, WinDbg a SOS při diagnostice memory-leaků

Problémem rychlého prozkoumávání velkých memory-dumpů v WinDbg/SOS je, že většinu zkoumání provádíme nad jednotlivými instancemi objektů. Vytipujeme si závadný typ (!dumpheap -stat) a pro pár instancí (!dumpheap -mt <MT>) zkoumáme, odkud pocházejí (!GCRoot <ObjAddr>). Pokud nemáme dost štěstí, po pár pokusech (které mohou být navíc při velkých memory-dumpech dosti časově náročné), nás to přestane bavit.

Nástrojem, jak provést v takovém případě hromadnou analýzu a pohlédnout na heap určitým agregovaným pohledem, je například CLR Profiler a jeho Heap Graph:

Ukazuje nám, jak na sebe jednotlivé typy navzájem referencují, a to vše v poměru k velikosti paměti (included, tedy včetně grafu referencovaných instancí). Vše je seskupeno po typech, nezabýváme se tedy jednotlivými instancemi.

Jak se k tomu dopracovat, mám-li memory dump

Z WinDbg/SOS je potřeba si vytvořit LOG-soubor pro CLR profiler pomocí příkazu

!TraverseHeap C:\output-path\name.log

…to samotné může nad velkým memory-dumpem hooodně trvat. Podle stroje a dumpu až hodiny. Na rozdíl od čekání na !GCRoot však dostanete výstup, který vás k cíli téměř jistě navede.

Následně již soubor načteme do CLR Profileru pomocí File / Open log file…

…zase čekáme a čekáme :-))

A na dialogu Summary for zvolíme Heap Graph

image

…čekáme a čekáme, až dostaneme třeba:

SNAGHTML96fdfd

Cache není nikdy dost ;-)

Mimochodem CLR Profiler nám dá i další zajímavé histogramy, které nám pomohou k pochopení poměrů v analyzovaném memory-dumpu. O nich možná někdy příště…

Viz též

WinDbg/SOS trik: Klikatelný výstup (DML)

Práci s  WinDBG/SOS lze zpříjemnit malým trikem. Pokud za název příkazu (většina to podporuje) připojíte přepínač /d, bude výstup klikatelný (spustí příkaz, který odpovídá prozkoumání daného cíle dle jeho typu):

image

Pokud se nám nechce /d za každý příkaz psát, můžeme ho zapnout globálně:

.prefer_dml 1

Vypnout lze samozřejmě nulou.

Oficiálně se to celé jmenuje „Debugger Mark Up Language“ (DML).

ClrMD: Nalezení statických rootů při analýze .NET memory-dumpu

Používáme-li WinDbg a jeho rozšíření SOS.dll (popř. SOSEX nebo PSSCOR4) k analýze memory-dumpu .NET procesů, pak jednou z komplikovanějších úloh je například nalezení statických rootů, tedy statických fieldů, které drží reference na prozkoumávanou instanci.

Že se jedná o zarootování instance ze statického fieldu je zpravidla zřejmé z výstupu !GCRoot, který pak vypadá například nějak takto:

0:000> !GCRoot 0000000acaf3e8f8 
HandleTable:
    0000000f159f19f8 (pinned handle)
    -> 0000000ec87f1be8 System.Object[]
    -> 0000000d11ac08c8 System.Action
    -> 00000010f5df1020 System.Object[]
    -> 0000000acaf3e8f8 System.Action

Found 1 unique roots (run '!GCRoot -all' to see all roots).

.NET si drží hodnoty statických fieldů v systémových object[] polích, nicméně dohledat, který konkrétní statický field je naším rootem, už není tak jednoduché (obvykle skončíme ve WinDbg u s-d nebo s-q prohledávání paměti a hledání další reference, atp.).

ClrMD: Microsoft.Diagnostics.Runtime

Naštěstí existuje a z NuGet se dá snadno nainstalovat .NET knihovna Microsoft.Diagnostics.Runtime (zkráceně ClrMD), která umožňuje inspekci a analýzu memory-dumpu z .NET kódu obdobně jako byste pracovali s SOS ve WinDbg.

…a jednou z věcí, které lze pomocí ClrMD snadno dostat je právě identifikace statického fieldu, který je rootovou referencí na prozkoumávaný objekt:

DataTarget dataTarget = DataTarget.LoadCrashDump(@"D:\path\to\memory-dump.DMP");
ClrRuntime runtime = dataTarget.CreateRuntime(@"D:\path\to\mscordacwks.dll");
ClrHeap heap = runtime.GetHeap();

foreach (ClrRoot root in heap.EnumerateRoots())
{
	if (root.Object == 0x0000000d11ac08c8) // adresa rootu, který chceme prozkoumat
	{
		// jen pro ukázku třeba:
		Console.WriteLine(root.Kind);
		if (root.Kind == GCRootKind.StaticVar)
			Console.WriteLine(root.Name);
	}
}

Pracujeme ideálně paralelně s WinDbg+SOS:

  • do CreateRuntime() dáváme cestu k příslušnému mscordacwks.dll, z WinDbg získáme přes .cordll -ve -u -l
  • adresu prozkoumávaného rootu známe z WinDbg (první po System.Object[], viz výše uvedený příklad z !GCRoot)
  • výše uvedený snippet stačí prsknout do ConsoleApp, do které nareferencujeme ClrMD z NuGet
        • je potřeba nastavit Build / Platform Target na x86 nebo x64, podle toho, z jaké platformy pochází memory-dump

Zkušenosti s přechodu na TFS a agilní techniky – záznam a slides [WUG Praha 1/2015]

Slides z přednášky 15.1.2015 pro Windows User Group Praha (WUG), kde jsme s kolegou Jirkou Kandou povídali i našich zkušenostech z přechodu HAVITu na TFS a agilní techniky (v období 08/2012 až 12/2014):

Z přednášky jsem pořizoval záznam, který najdete na našem HAVIT YouTube Channelu:

Omluvte sníženou kvalitu zvuku záznamu, nahrávali jsme tentokrát na dva různé mikrofony a výsledný záznam je sestřihán ze dvou různých zvukových stop (jedna z mikrofonů navíc nebyl zřejmě úplně ideálně umístěn a chytal nežádoucí ruchy).

Konverze časové zóny ve webové službě

Datový typ DateTime má v sobě položku DateTimeKind, která může nabývat hodnot Local, Utc, Unspecified. Podle nastavení této hodnoty je po-té provedena konverze časové zóny při volání webové služby.

V případě kindu unspecified se konverze časové zóny neprovádí. V případě kindu local se převede časová zóna volající strany do časové zóny příjemce. V případě kindu UTC se čas také nepřevádí.

Kind se při přenosu nemění.

Zde jsou příklady, jak která operace v C# nastavuje DateTimeKind:

DateTime dt1 = DateTime.Now; // DateTimeKind.Local
DateTime dt2 = DateTime.UtcNow; // DateTimeKind.Utc
DateTime dt3 = new DateTime(2014, 1, 1); // DateTimeKind.Unspecified
DateTime dt4 = DateTime.Now.ToLocalTime(); // DateTimeKind.Local
DateTime dt5 = DateTime.Now.ToUniversalTime(); // DateTimeKind.Utc
DateTime dt6 = DateTime.SpecifyKind(DateTime.Now.ToUniversalTime(), DateTimeKind.Unspecified); // DateTimeKind.Unspecified, pouze změna kindu bez konverze času

Při přenosu času jsme nakonec použili toto řešení:

Struktura:
...
public class WorkLogItemDTO
{
  /// <summary>
  /// Datum pracovního záznamu.
  /// </summary>
  public DateTime Datum { get; set; }

  /// <summary>
  /// Identifikátor klientské časové zóny.
  /// </summary>
  public string TimeZoneID { get; set; }
...
 
Klient (při volání serveru):
...
dto.Datum = item.Datum;
dto.TimeZoneID = TimeZoneInfo.Local.Id;
...

Server:
...
if (item.Datum.Kind != DateTimeKind.Unspecified)
{
  if (string.IsNullOrEmpty(item.TimeZoneID))
  {
    throw new Exception("Casova zona neni vyplnena (WorkLogItemDTO->IdTimeZone).");
  }
  TimeZoneInfo clientTimeZone = TimeZoneInfo.FindSystemTimeZoneById(item.TimeZoneID);
  item.Datum = TimeZoneInfo.ConvertTime(item.Datum, clientTimeZone);
}
...

 

Azure WebJob se občas spouští dvakrát

Problém

Máme v Azure nasazeno několik Azure Website spolu s naplanovanými (scheduled) web job. Řešíme situaci, kdy se webjob spustí dvakrát, zhruba v rozmezí jedné minuty. (V našem konkrétním případě posíláme notifikační emaily a tyto nám přijdou dva.)

Analýzou historie zjišťuji, že scheduler se webjob pokusil úlohu spustit dokonce třikrát.

webjob-history

Přičemž log jednotlivých pokusů vypadá takto:

  1. Http Action – Request to host ‚(…).scm.azurewebsites.net‘ failed: The timeout (30 seconds) was reached.
  2. Http Action – Response from host ‚(…).scm.azurewebsites.net': ‚Conflict‘ Response

    Body: Cannot start a new run since job is already running.
  3. Http Action – Response from host ‚(…).scm.azurewebsites.net': ‚Accepted‘ Response

Položky logu interpretuji takto:

  • Bod 1 úspěšně spustit webjob, ale nebyl schopen v intervalu 30 sekund toto zjistit.
  • Bod 2 se pokusil spustit webjob, ale ten již běžel, takže byl oznámen konflikt.
  • Bod 3 se opět pokusil spustit webjob, ten již doběhl, takže byl webjob úspěšně (ve skutečnosti podruhé) spuštěn.

Řešení

Po přečtení

se pokouším interpretovat (snad) klíčovou větu „If your site runs continuous web jobs, you should enable Always On, or the web jobs may not run reliably“ tak, že Always On zapínám i pro náš scheduled Web Job. Pomáhá to.

Volba Always On se nastavuje v Azure portálu, v nastavení Azure Website, se přepínačem Always On na záložce Configure.