Category Archives: Azure

Debugging Story: Z Azure WebJobs dashboardu nelze spustit funkci s DateTime parametrem

V krátkosti se podělím o svůj poslední debugging zážitek – ani ne proto, že by byl výjimečný svým zjištěním, ale spíše jako inspiraci pro techniky a postupy, které lze při debuggingu použít.

Symptom

Problém se projevoval tím, že z Azure WebJobs Dashboardu (Kudu) se nám nedařilo spouštět funkci, které jsme potřebovali jako vstupní parametr předat hodnotu typu DateTime:

clip_image002

Spuštění končilo chybou System.FormatException: String was not recognized as a valid DateTime:

clip_image002[5]

Architektura řešení

WebJobs SDK umožňuje, abyste vytvořili WebJob, který v sobě má funkce (Functions). Tyto mohou být spouštěny podle časového plánu, triggerovány událostí na straně Azure (např. nová message v Queue, nový soubor na disku, nový záznam v DB tabulce, atp.), nebo spouštěny ručně pomocí UI Azure WebJobs Dashboardu (součást Kudu, resp. jeho automatické rozšíření).

V našem případě se jednalo o ruční spouštění funkce z Azure WebJobs Dashboardu, které na pozadí funguje tak, že s WebJobs hostem (vaším procesem – WebJobem – který v sobě má aktivní JobHost z WebJobs SDK) komunikuje prostřednictvím Azure Queue.

1. Kontrola formátu data na vstupu

Na začátku člověk samozřejmě pochybuje o tom nejjednodušším – tedy jestli zadává do UI WebJobs Dashboardu hodnotu ve správném formátu. Je to samozřejmě webový formulář, který musí string zapsaný do textboxu nějak parsovat na DateTime.

Hledal jsem tedy ve zdrojácích WebJob SDK (GitHub), jak se vstup na DateTime převádí. Protože už jsem sám implementoval binding vlastního typu (aby bylo možno pohodlně volat z UI funkci, která potřebuje typ na vstupu), celkem rychle jsem se donavigoval na StringToDateTimeConverter, ve kterém je

public DateTime Convert(string input)
{
       return DateTime.ParseExact(input, "O", CultureInfo.InvariantCulture,
           DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
}

Aby si ověřil, že mám správnou podobu vstupu, použil jsem nový C# Interactive z Visual Studia 2015 (stejně snadno by pomohl i LINQPad):

image

…zde tedy zakopaný pes nebude, vstup máme správně.

2. Zjištění, jaký string vstupuje do konverze DateTime.ParseExact

Metoda DateTime.ParseExact(..) je poměrně přísná na vstupní formát. Vadí jí i trailing whitespace a podobné “drobnosti”, tedy jsem se vydal cestou zjišťování, co do této metody vstupuje, a kde se to po cestě “pokazí”.

Bohužel situace není úplně jednoduchá – WebJobs SDK a jeho komunikace s Dashboardem nám zde běží v prostředí Azure a není úplně triviální vše napojit z lokálního prostředí a debugovat klasicky ve Visual Studiu (byť by asi nebyl problém lokální instanci nasměrovat na příslušný Azure Storage Account). Nechtělo se mi řešit tuto cestu, která by mi stejně umožnila ladění jen na straně WebJobs hosta, vydal jsem přes analýzu produkčního prostředí Azure.

Mým prvním cílem bylo získat memory-dump WebJobs hosta a zkusit v něm najít, jaký string se parsuje. Vzhledem k tomu, že můj WebJobs host nebyl v danou chvíli zatížen jinou aktivitou, nezdržoval jsem se nastavováním podmínek, za kterých chci memory-dump sejmout (např. při výjimce FormatException, nebo bych mohl zvolit breakpoint do metody Convert, atp.). Rozhodl jsem se to risknout, použít prostý ručně sejmutý memory-dump a doufat, že Garbage Collector mi příslušné instance ještě z paměti nevyhodil, a že v něm něco najdu.

Po prvním neúspěšném pokusu s Kudu Process Explorerem, který mi sejmul bezcenný 64-bitový dump mého 32-bitového procesu, jsem získal dump přes Debug Consoli spuštěním SysInternals ProcDump (viz samostatný post).

Pro prohledávání memory-dumpu se výborně hodí NetExt rozšíření WinDbg se svým !wfrom:

0:000> .symfix
0:000> .reload
0:000> .load c:\Windows\Microsoft.NET\Framework\v4.0.30319\sos
0:000> .load netext
0:000> !wfrom -type System.String where ( $contains($string(),"2016-05-14") ) select $addr(), $string()

0 Object(s) listed
71,537 Object(s) skipped by filter

…nicméně žádný řetězec, který by v sobě měl “2016-05-14” se v paměti nenašel.

Nenechal jsem se odradit (byť už jsem podezíral GC) a zkusil hledat jen “2016”:

0:000> !wfrom -type System.String where ( $contains($string(),"2016") ) select $addr(), $string()
..
calculated: 05/14/2016 15:05:23 +02:00
calculated: 0737EC64
calculated: 05/14/2016 15:05:23 +02:00
calculated: 073C1E58
...
50 Object(s) listed
71,487 Object(s) skipped by filter

…padesát stringů s “2016” nalezeno (výpis zkrácen) a mezi nimi i dva, které mě velmi zajímaly. Jsou to přesně moje vstupní hodnoty, jenom jinak formátovány. Evidentně se jedná o stringy, které se DateTime.ParseExact(…) snažil neúspěšně parsovat.

3. Zjišťování, kde se stringová reprezentace data a času změní

V tuto chvíli již bylo více než pravděpodobné, že se jedná o bug ve WebJobs SDK, nicméně mi to nedalo, abych se v tom nepovrtal dále. Chtěl jsem co nejlépe lokalizovat místo, kde dojde k poničení stringu s datem.

Procházel jsem tedy ve zdrojových kóde WebJobs SDK cestu, kterou tečou argumenty od UI Dashboardu (FunctionController.cs – GetArguments(), Invoke()), přes odesílání zprávy do Azure Queue (HostMessaging/Invoker.cs) a dále.

Protože jsem měl k dispozici memory dump WebJobs Hosta, který z Azure Queue naopak zprávy vyzvedává a vykonává, chtěl jsem zkontrolovat, jakou zprávu z queue vyzvedne a jestli už v ní jsou argumenty poškozeny. Ze zdrojových kódů je zřejmé, že komunikace proběhne zasláním zprávy typu CallAndOverrideMessage, která v sobě má IDictionary<string, string> argumentů. Použil jsem tedy !DumpHeap s filtrem na typ a následně i !wdict z NetExt pro přehledné vypsání obsahu Dictionary:

0:000> !DumpHeap -type CallAndOverrideMessage
 Address       MT     Size
01e17eb4 060223fc       60     
01e18448 06073050       56     
07380594 06073050       56     
073c2d78 060223fc       60     

Statistics:
...
0:000> !do 01e17eb4
Name:        Microsoft.Azure.WebJobs.Host.Protocols.CallAndOverrideMessage
MethodTable: 060223fc
EEClass:     06018c14
Size:        60(0x3c) bytes
File:        D:\local\Temp\jobs\continuous\JobScheduler\3pfsjnlr.4ry\Microsoft.Azure.WebJobs.Host.dll
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
72cbd834  4000001        4        System.String  0 instance 01e17d4c <Type>k__BackingField
72cba91c  4000018       14          System.Guid  1 instance 01e17ec8 <Id>k__BackingField
72cbd834  4000019        8        System.String  0 instance 01e17d78 <FunctionId>k__BackingField
7289231c  400001a        c ...tring, mscorlib]]  0 instance 01e17ef0 <Arguments>k__BackingField
06022340  400001b       10         System.Int32  1 instance        2 <Reason>k__BackingField
72c6cb20  400001c       24 ....Guid, mscorlib]]  1 instance 01e17ed8 <ParentId>k__BackingField
0:000> !wdict 01e17ef0 
Items   : 3
[0]:==============================================(Physical Index: 2)
System.__Canon key = 01e17e98 dateTo
System.__Canon value = 01e1816c 05/14/2016 15:05:23 +02:00
[1]:==============================================(Physical Index: 0)
System.__Canon key = 01e17e34 businessKind
System.__Canon value = 01e17e5c Onshore
[2]:==============================================(Physical Index: 1)
System.__Canon key = 01e17e78 dateFrom
System.__Canon value = 01e17f80 05/14/2016 15:05:23 +02:00

Zde se ukázalo, že příchozí zpráva z Azure Queue, resp. její deserializace už v sobě chybný změněný formát data má.

Nedalo mi to a hledal jsem, co přesně teče přes Azure Queue. Chvilku jsem se marně snažil zachytit zprávu ve Visual Studiu přes Cloud Explorer, až jsem se nakonec vrátil ke zdrojovým kódům WebJobs SDK a jejich implementaci příjmu zpráv z Azure Queue přes StorageQueueMessage, která v sobě (resp. přes CloudQueueMessage z Azure Storage) drží raw-stringovou reprezentaci zprávy:

0:000> !DumpHeap -type CloudQueueMessage /d
 Address       MT     Size
0134bae0 057d7fa0       12     
01404e24 05b30b38       12     
01404e30 05b31034       12     
01459908 04ada834       32     
01459e2c 04add3c0       12     
01459e90 04adac64       32     
01e17b34 05b30e58       80     
0734bd5c 05b30e58       80     
07473d64 05b30e58       80     

Statistics:
...
0:000> !DumpObj /d 07473d64
Name:        Microsoft.WindowsAzure.Storage.Queue.CloudQueueMessage
MethodTable: 05b30e58
EEClass:     057c4180
Size:        80(0x50) bytes
File:        D:\local\Temp\jobs\continuous\JobScheduler\3pfsjnlr.4ry\Microsoft.WindowsAzure.Storage.dll
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
72cbd834  4000141        4        System.String  0 instance 07473828 <Id>k__BackingField
72cbd834  4000142        8        System.String  0 instance 07473880 <PopReceipt>k__BackingField
72c696f4  4000143       1c ...ffset, mscorlib]]  1 instance 07473d80 <InsertionTime>k__BackingField
72c696f4  4000144       2c ...ffset, mscorlib]]  1 instance 07473d90 <ExpirationTime>k__BackingField
72c696f4  4000145       3c ...ffset, mscorlib]]  1 instance 07473da0 <NextVisibleTime>k__BackingField
72cbf6bc  4000146       14         System.Int32  1 instance        1 <DequeueCount>k__BackingField
05b30da4  4000147       18         System.Int32  1 instance        1 <MessageType>k__BackingField
72cbd834  4000148        c        System.String  0 instance 074738c8 <RawString>k__BackingField
72cc2518  4000149       10        System.Byte[]  0 instance 00000000 <RawBytes>k__BackingField
72cbab18  400013f      150      System.TimeSpan  1   static 021f4f28 MaximumTimeToLive
72cba5a0  4000140      154 ...Text.UTF8Encoding  0   static 0734bfb4 utf8Encoder
0:000> !DumpObj /d 074738c8
Name:        System.String
MethodTable: 72cbd834
EEClass:     728ff344
Size:        1166(0x48e) bytes
File:        D:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String:      ew0KICAiVHlwZSI6ICJDYWxsQW5kT3ZlcnJpZGUiLA0KICAiSWQiOiAiMmExYzYwNjMtNmFkZC00MmE1LWE1M2EtNjZhYmM2MTY3NmI5IiwNCiAgIkZ1bmN0aW9uSWQiOiAiSGF2aXQuWGVyb3hXZWJUb29sLkpvYlNjaGVkdWxlci5Kb2JzLkVycFN5bmMuUXVlcmllc0Rvd25sb2FkRXJwU3luY0pvYi5FeGVjdXRlRGF0ZVJhbmdlIiwNCiAgIkFyZ3VtZW50cyI6IHsNCiAgICAiYnVzaW5lc3NLaW5kIjogIk9uc2hvcmUiLA0KICAgICJkYXRlRnJvbSI6ICIyMDE2LTA1LTE0VDE1OjA1OjIzLjk4ODA4MTQrMDI6MDAiLA0KICAgICJkYXRlVG8iOiAiMjAxNi0wNS0xNFQxNTowNToyMy45ODgwODE0KzAyOjAwIg0KICB9LA0KICAiUmVhc29uIjogIkRhc2hib2FyZCIsDQogICJQYXJlbnRJZCI6ICJiYjQyYWYwYS00MTcyLTRiYjUtYTRiMS01ZThiMzQwMDU1MDkiDQp9
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
72cbf6bc  4000243        4         System.Int32  1 instance      576 m_stringLength
72cbe278  4000244        8          System.Char  1 instance       65 m_firstChar
72cbd834  4000248       40        System.String  0   shared   static Empty
    >> Domain:Value  0095f5d0:NotInit  <<

Ze zdrojových kódů Azure Storage CloudQueueMessage.AsString je zřejmé, že v RawString je base64, který snadno převedeme pomocí https://www.base64decode.org/:

image

…zde se překvapivě ukazuje, že JSON serializace zprávy má v sobě korektní (původní) podobu data, a že problém tedy je v konverzi z CloudQueueMessage, resp. StorageQueueMessage na CallAndOverrideMessage. Ve zdrojácích už nebyl problém dohledat, že se tak děje v HostMessageExecutor pomocí JsonConvert deserializace:

HostMessage model = JsonConvert.DeserializeObject<HostMessage>(value.AsString, JsonSerialization.Settings);

CallAndOverrideMessage callAndOverrideModel = model as CallAndOverrideMessage;

4. Neumí tedy Newtonsoft Json.NET správně deserializovat string?

WebJobs SDK používá pro HostMessage customizovaný JsonConvertor, při jehož použití se poškození stringu s datem projeví. Pokud se convertor odebere a ponechá se výchozí serializace+deserializace, problém se neprojeví.

Více do hloubky už jsem nešel, protože písmenko “J” na začátku názvu vývojářské technologie je pro mě bránou do pekla, kterou jsem nechtěl projít. Ke problému jsem vytvořil jednoduché repro a nareportoval issue WebJobs SDK týmu.

Azure Web Apps: Získání memory-dumpu procesu (Web, WebJob)

Pro hlubší diagnostiku je někdy potřeba získat memory-dump procesu. Ukážeme si teď, že to jde i z Azure Web Apps (WebSites), včetně jejich WebJobs.

Kudu Process Explorer

Nejjednodušší situaci máme, pokud chceme získat aktuální memory-dump 64-bitového procesu. Stačí otevřít management-site Kudu (to je ten webík, který najdete na adrese https://yoursitename.scm.azurewebsites.net), v Process Exploreru pravým tlačítkem myši otevřít kontextové menu příslušného procesu (kromě w3wp.exe jsou tam i WebJoby) a z něj vybrat Download Memory Dump:

image

…memory-dump se začne po chvilce (někdy i po několika minutách!) stahovat.

Kudu Debugging Console + ProcMon ze SysInternals

Nejobvyklejší komplikací je, pokud nám na 64-bitovém stroji (App Service Plan) běží 32-bitový proces (což je pro Azure Web Apps poměrně obvyklé, chceme šetřit vzácnou pamětí). Výše popsaný postup přes Process Explorer nám vytvoří 64-bitový dump 32-bitového procesu, s kterým mnoho muziky nenaděláte, byť třeba Visual Studio si s ním celkem poradí (stejný problém, jako když použijete pro vytvoření dumpu 64-bitový TaskManager).

Naštěstí Kudu má kromě Process Exploreru i Debug Console – CMD i PowerShell. Navíc v cestě D:\devtools\sysinternals\ najdete na WebApps stroji předinstalované utility SysInternals vč. ProcDump (dříve bývalo potřeba ProcDump stáhnout, ale už ani to není potřeba).

image

Je zde opět jedna záludnost. ProcDump je potřeba spouštět s přepínačem -accepteula, protože jinak se při svém prvním spuštění dožaduje akceptace EULA v GUI (což nevidíte), a dokud licenční smlouvu neakceptujete, otravuje s tím stále (což se v konzoli projevuje tím, že ProcDump nic nedělá).

ProcDump lze samozřejmě použít kromě prostého získání aktuálního dumpu i v mnoha pokročilejších scénářích – obdobně jako DebugDiag mu lze říci podmínky, na jejich splnění má čekat a dump vytvořit. Viz ProcDump reference.

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”.

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.

Přístup k Team Foundation Serveru z Azure Website

Problém

V jedné z aplikací přistupujeme pomocí API jako klient do Team Foundation Serveru 2013.

ICredentials credentials = new NetworkCredential(...);
TfsTeamProjectCollection collection = new TfsTeamProjectCollection(new Uri(...), credentials);
collection.EnsureAuthenticated();

Po migraci aplikace z vlastních serverů do Azure Website nám připojení přestalo fungovat, konkrétně začalo docházet k této výjimce:

System.IO.IOException: The specified registry key does not exist.
   at Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str)
   at Microsoft.Win32.RegistryKey.CreateSubKeyInternal(String subkey, RegistryKeyPermissionCheck permissionCheck, Object registrySecurityObj, RegistryOptions registryOptions)
   at Microsoft.Win32.RegistryKey.CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions options)
   at Microsoft.VisualStudio.Services.Common.TokenStorage.RegistryTokenStorageHelper.GetRootKey(String subkeyName)
   at Microsoft.VisualStudio.Services.Common.TokenStorage.RegistryTokenStorage.RetrieveToken(VssTokenKey tokenKey)
   at Microsoft.VisualStudio.Services.Common.TokenStorage.VssTokenStorage.Retrieve(VssTokenKey tokenKey)
   at Microsoft.TeamFoundation.Client.TfsClientCredentialStorage.RetrieveToken(Uri serverUrl, VssCredentialsType credentialType)
   at Microsoft.TeamFoundation.Client.CookieCredential.OnCreateTokenProvider(Uri serverUrl, HttpWebResponse response)
   at Microsoft.TeamFoundation.Client.IssuedTokenCredential.CreateTokenProvider(Uri serverUrl, HttpWebResponse response, IssuedToken failedToken)
   at Microsoft.TeamFoundation.Client.TfsClientCredentials.TryGetTokenProvider(Uri serverUrl, IssuedTokenProvider&amp; provider)
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpRequestHelpers.PrepareWebRequest(HttpWebRequest webRequest, Guid sessionId, String operationName, CultureInfo cultureInfo, TfsRequestSettings settings, TfsClientCredentials credentials, IdentityDescriptor impersonate, IssuedToken&amp; currentToken, IssuedTokenProvider&amp; tokenProvider)
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpRequestHelpers.CreateSoapRequest(Uri requestUri, Guid sessionId, String soapAction, String operationName, CultureInfo cultureInfo, TfsRequestSettings settings, TfsClientCredentials credentials, IdentityDescriptor impersonate, IssuedToken&amp; currentToken, IssuedTokenProvider&amp; tokenProvider)
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpWebRequest.CreateWebRequest()
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpWebRequest.SendRequest()
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpRequestChannel.Request(TfsMessage message, TimeSpan timeout)
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpClientBase.Invoke(TfsClientOperation operation, Object[] parameters, TimeSpan timeout, Object[]&amp; outputs)
   at Microsoft.TeamFoundation.Framework.Client.Registration.GetRegistrationEntries(String toolId)
   at Microsoft.TeamFoundation.Framework.Client.RegistrationProxy.GetRegistrationEntries(String toolId)
   at Microsoft.TeamFoundation.Framework.Client.RegistrationService.RefreshMemoryCache()
   at Microsoft.TeamFoundation.Framework.Client.RegistrationService.RefreshCachesIfNeeded(Boolean direct)
   at Microsoft.TeamFoundation.Framework.Client.RegistrationService.GetRegistrationEntries(String toolId)
   at Microsoft.TeamFoundation.Framework.Client.PreFrameworkServerDataProvider.FindServiceLocation(String serviceType, String toolId)
   at Microsoft.TeamFoundation.Framework.Client.PreFrameworkServerDataProvider.LocationForCurrentConnection(String serviceType, Guid serviceIdentifier)
   at Microsoft.TeamFoundation.Client.TfsConnection.EnsureProviderConnected()
   at Microsoft.TeamFoundation.Client.TfsConnection.EnsureAuthenticated()

TFS klient v průběhu přihlašování šahá do registrů. Po dlouhém pátrání pomocí .NET Reflectoru jsem dospěl k názoru, že v nich hledá dříve uložený autentizační token. To pro nás není podstatné, důležité je, že se bez toho můžeme obejít.

Řešení

Řešením je použití třídy TfsClientCredentials. Tu vyrobíme pomocí WindowCredentials a tu s pomocí dvou tříd: NetworkCredentials a SimpleWebTokenCredential, přičemž v NetworkCredentials máme údaje k autorizaci a SimpleWebTokenCredential nemusí nést žádné hodnoty, pouze zajišťuje workaround popsaného problému.

NetworkCredential networkCredentials = new NetworkCredential(...)
TfsClientCredentials tfsCredentials = new TfsClientCredentials(new WindowsCredential(networkCredentials), new SimpleWebTokenCredential(null, null), allowInteractive: false);
TfsTeamProjectCollection collection = new TfsTeamProjectCollection(new Uri(...), tfsCredentials);
collection.EnsureAuthenticated();

Azure Website: Pokus o stažení souboru *.woff souboru končí chybou 404, přestože existuje

Abych byl spravedlivý, nejde jen o Azure Website, ale i o Windows Servery 2008. Důvodem je nedefinovaný Content-Type (resp. MimeType) pro daný typ.

Ten můžeme přidat do web.configu:

<system.webServer>
<staticContent>
<mimeMap fileExtension=".woff" mimeType="application/x-woff" />
</staticContent>
</system.webServer>

Pokud však takovouto konfiguraci nasadíme na server, kde je již Content-Type (resp. MimeType) pro danou příponu nastaven, dojde k pádu aplikace. Zobrazená chyba nám oznámí, že se pokoušíme nastavit MimeType pro typ, který je již definován.

Pokud chceme mít jednu konfiguraci pro všechny serveru, ať již mají nebo nemají definovaný MimeType pro příponu woff, můžeme použít trik s remove:

<system.webServer>
<staticContent>
<remove fileExtension=".woff" />
<mimeMap fileExtension=".woff" mimeType="application/x-woff" />
</staticContent>
</system.webServer>

Azure: Nedaří se vytvořit databázi importem z *.bacpac

Při pokusu o založení nové databáze importem z *.bacpac v portálu se zobrazuje potvrzení, že přístup do Azure Storage je v pořádku a toto oznámení je následováno chybou oznamující, že nelze vložit požadavek na import databáze.

Heslo k databázovému serveru obsahovalo diakritiku, nápad zkusit heslo bez diakritiky se osvědčil –  po změně hesla funguje import bez problémů.

Na Azure mi nešla založit DB ve WestEurope, SQL server ano, jiné regiony ano (Azure Free Trial?)

Krátká empirická zkušenost.

Symptoms

Nová subscription, která vznikla tak nějak mimoděk, jako první subscription k danému organizational accountu a při zakládání této první subscription se to na nic neptalo a založilo to Trial Subscription s kreditem 150 EUR (pěkně děkuji, viz níže).

No nic, víc jsem to nezkoumal a prostě jen vypnul spending limit s tím, že se z toho stane běžná Pay-As-You-Go (

Zakládáme do subscription nové služby. WebSites v pohodě, WebJobs v pohodě, SendGrid v pohodě, při zakládání nové databáze „starý“ portal (manage.windowsazure.com) založí databázový server, ale k tomu řekne Could not create database ‚XY‘:

clip_image002

V detailech zprávy je výstižné upřesnění: Could not create database ‚XY‘.

Nejpozoruhodnější na tom je, že úplně stejná databáze v regionu East Asia se bez problémů vytvoří. Ostatní služby WestEurope OK.

Při použití nového portálu (portal.azure.com) je chování stejné, jen v detailech problému je cosi o „not supported for your subscription“ (WTF!). Na homepage všechny uzly Azure zelené, žádný výpadek není hlášen.

?Resolution?

Běžné Googlení „Could not create database“ dává diskuzní příspěvky o tom, že se něco v Azure nezpropagovalo a nezbývá než kontaktovat Azure Support. Pay-As-You-Go však má support omezený a nechce se mi čekat, než se to někam pohne. Zakládám tedy raději novou druhou subscription (tentokrát se to již ptá na subscription type a volím explicitně Pay-As-You-Go) a po chvilce usazování (napoprvé to opět nešlo, ale pro změnu nešel založit ani SQL server) vše úspěšně funguje.

?Cause?

Pokud i nyní zkusím na té prvně vytvořené subscription (která se pořád na různých místech identifikuje jako Trial a zřejmě tedy odstraněním spending limitu nedojde k přepnutí na produkční) založit databázi do WestEurope, dojde pouze k založení DB serveru, ale založení DB je zamítnuto.

Celé to zavání nějakým nedostatkem instancí v datovém centru WestEurope, což by vysvětlovalo i skutečnost, že subscription, která se označuje jako Trial, je na tom hůře, než následně založená Production. Divné jenom je, že na té Trial subscription to založí server a „jenom“ na něm nedovolí založit DB (i když i to nějakou logiku má).

Závěr

Přesnou příčinu neznám a více času s tím ztrácet nechci. Celé to do KnowledgeBase zapisuji proto, že nemusím být sám, kdo bude takovým „Free Trial“ obšťastněn.

Někdo máte podobnou/jinou zkušenost? Někdo máte lepší vysvětlení?

Azure WebSites – rozcestník pro počáteční orientaci

Nejprve jsem myslel, že to dám k nám na interní wiki, ale mohlo by se to hodit i dalším…