Soci (Soczó Zsolt) szakmai blogja

2013.01.13.

HashSet.Add végtelen ciklus?

Filed under: .NET,C#,CLR,Szakmai élet — Soczó Zsolt @ 22:52

HashSetbe akartam berakni hibernate entitásokat, amelyek Equals-sza és GetHashCode-ja így nézett ki:

public virtual bool Equals(EntityBase other)
{
if (other == null)
{
return false;
}
if (Id == 0)
{
//Transient object
return ReferenceEquals(this, other);
}
return other.Id == Id;
}

public override bool Equals(object obj)
{
if (!(obj is EntityBase))
{
return false;
}
return Equals((EntityBase)obj);
}

public override int GetHashCode()
{
return Id;
}

A HashSet.Add-nak beadva egy ilyen entitás példányt az Add soha nem tért vissza, végtelen ciklusba került. Miért?

2012.07.30.

BCLExtensions

Filed under: .NET,.NET 4,C#,CLR,Szakmai élet — Soczó Zsolt @ 11:03

Pár apró, de hasznos dolog van benne, nekem a rendezett IListben történő BinarySearch kellett belőle.

Eddig ezt csináltam:

int pos = ArrayList.Adapter(this).BinarySearch(dateTime, new Bar2DateTimeComparer());

Most már így, nem kell adaptert ráhúzni, és generikus a comparer hívás, így egy kicsit gyorsabb is:

int pos = this.BinarySearch(dateTime, new Bar2DateTimeComparer());

2012.07.23.

SP vs. OR mapper

Az előző bejegyzésből a kommentek alapján lehet az jött le, hogy én minden dolgot OR mapperrel valósítanék meg. Csudákat, szó sincs erről.

Nézzünk egy példát. Az előző architektúra tervemben (ami prototípus majd framework is lett, nem csak terv) NHibernate és Oracle alapon készült az app. Sok más mellett tetszőleges országban használható emberneveket kellett tudni tárolni, ezért a db terv elég általános lett. Person-PersonName-PersonNamePart, ez a 3 tábla volt a fő váz. PersonNameType, PersonNamePartType, ezek enum jellegű lookup táblák.
Egy lekérdezés célja visszaadni tetszőleges név komponens alapján a beteg nevét. Azaz a PersonNamePart tábla egy oszlopában kellett keresni, ez jól indexelhető, hatékony lekérdezés. Utána jött a mókásabb rész. A PersonNamePartból vissza kellett navigálni a PersonName-re, onnan a Personre, majd a megjelenítés miatt vissza le az összes PersonName-re és onnan tovább a PersonNamePartra.
Ezt a lekérdezést először megírtam P/L SQL-ben, hogy lássam, kb. mit akarok csinálni, ez mennyire hatékony, kell-e hozzá Cluster (nem clustered, ez oracle, ez teljesen más) index az IO csökkentésére, stb.
Amikor a lekérdezéssel elégedett voltam, elkezdtem átírni az NHibernate LINQ providerére. A célom az volt, hogy a query gondolkodásmódját pontosan visszatükrözze a LINQ query, így remélhetőleg a generált sql is hasonlóan hatékony lesz. Sajnos azonban a kísérlet kudarc volt. Az NHibernate LINQ providere egyszerűen nem implementálta amit én akartam (ha jól emlékszem Take(n) után Select). Megnéztem a forrását, ott volt egy nagy büdös throw new NotImplementedException().
Mi maradt? Sp, aminek a kimeneti joinolt sorait az NHibernate alakította vissza entitásokká (ez viszont nagyon ügyes része az NHibnek.)
Azaz a lekérdezés a lehető leghatékonyabb volt, de a végén mégis a domain modellel dolgoztam, ami végül szimpatikus hibrid lett, bár jobb lett volna tisztán LINQ-ban megírni.
Mi lett volna, hogy rendesen megírták volna a LINQ providert? Megnéztem volna a generált SQL-t, és ha az megfelelően hatékony lett volna, akkor maradt volna az.
Ha nincs rá különösebb okom, én szeretem a logikákat C#-ban írni. Az objektumorientáltság miatt jól lehet szervezni az összetettebb kódokat, sokkal jobban, mint a procedurális TSQL-ben.
Lehet memóriában cache-lni dolgokat, SQL Serveren belül nem nagyon. Az appszervereket sokkal könnyebb horizontálisan skálázni mint a DB-t. Magyarul egymás mellé berakni sok azonos szervert, még ha ez marhára nem is triviális, de legalább lehetséges. Emiatt a DB-ről terhelést levenni nem kell félnetek, mindenképpen üdvös tevékenység.
Viszont, ha az adatokhoz közel kell műveleteket végezni, akkor nem fogok feleslegesen átvinni nagyszámú sort az appszerverre. Ha egy gyakran futó kritikus tranzakció hossza fontos, akkor lehet spbe rakom. Ha sok sort érint a művelet nem hozom ki őket a dbből.

Próbálok pár példát konstruálni, illusztrálandó az előbbieket.

1. Be kell olvasni 100 sort egy táblából, lefuttatni valami összetett műveletet rajta, amely kb. 1000 sornyi programkódnak felel meg, majd az eredményt visszaírni egy másik táblába, amely kb. 20 sornyi insertet jelent. OR mapper. A bonyolult logika valószínűleg erősebb érv lesz, mint az, hogy 100 sort át kell vinni a dróton, illetve, hogy 2 roundtrippel jár a művelet. Mivel a logika C#-ban lesz, könnyű lesz teszteket írni rá (a db könnyen kifake-elhető), könnyebb lesz karbantartani, ha módosítani kell.
Ha esetleg az egésznek egy tranzakcióban kell lenni, és a round tripek ideje jelentősen nyújtja a tranzakciók futási idejét, ami miatt az esetleg szigorúbb izolációs szinten futó tranzakciók elkezdenek torlódni akkor, és csakis akkor kezdenék el filózni az spn. De ezt csak méréssel lehet eldönteni, mert az sp esetén meg valószínűleg a logika lesz lassabb, így lehet többet vesztünk, mint nyerünk.

2. Be kell olvasni 5 táblából 1 sort, ezek alapján dönteni, majd visszaírni 2 táblába 1-1 sort. Az alapeset ugye az, hogy megpróbáljuk OR mapperen keresztül megoldani a dolgot, majd belátni, hogy az elég hatékony-e, ha nem akkor áttérünk sp-re. NHibernate vagy EF Extension Pack Future-ökkel az 5 select valószínűleg kiadható 1 roundtrip alatt, ha nincs közöttük függőség. Ha van, akkor lehet, hogy join-nal vagy navigácós relációkon haladva még mindig megoldható 1 roundtrip alatt. Ha nem, akkor nem. A visszaírások normális OR mapper esetén 1 roundtrip alatt végrehajthatók (batching), így 2 roundtrip alatt megvan a feladat. Valószínűleg bőven belefér, marad az OR mapper.

3. Le kell törölni sok sort, ahol a DueDate nem mai dátum. Egyértelműen sp, semmi értelme OR mapperbe behúzni a törlendő sorokat, könnyedén megfogalmazható a feltétel egy sima SQL where-ben. Hozzáteszem, NHibbel lehet delete és update műveleteket is kiadni entitás fogalmak használatával, így annál ezt se spzném, a generált sql az lenne, ami kézzel beírnék az spbe, csak direkt sqlként. Akkor meg minek sp-zzek?

4. Be kell szúrni 500 sort, nagyon gyorsan, naponta sokszor. pl. laborgépek okádják magukból az eredményeket, ezeket kell eltárolni. Ez már érdekesebb kérdés. Első körben azt mondanánk, OR mapper erre nem való. Az első gondolatom az ilyenre valamilyen bulk copy megoldás lenne, pl. SQL Server esetén SqlBulkCopy osztállyal. De pl. Oracle esetén van un. Array Binding, amivel a bulk műveletekkel közel azonos teljesítményt lehet elérni, a bulk műveletek limitációi (nem lehet trigger a táblán, stb.) nélkül. Ami meglepő, hogy a NHibernate oracle esetén kihasználja az array bindingot, és nem csak egyszerűen batcheli a műveleteket, de array bindinggel küldi be. Emiatt veszett gyorsan tud beszúrni, így NHib esetén simán OR mappert használnék erre is. Egy konkrét mérésemben 10000 sor beszúrása így nézett ki Oracle-be (fejből mondom, de a nagyságrendek jól lesznek):
1. NHibernate identity id generator (othodox SQL Serveres gondolkodásmód): 52 sec
2. ADO.NET soronkénti insert: 12 sec
3. NHibernate hilo id generátorral: 2.7 sec
4. ADO.NET Array Bindinggal: 2.2 sec
5. Direct Path Loading: 2.1 sec

Mik a tanulságok ebből a mérésből?
A. Az identity alapú ID generálás megöli az insertek teljesítményét (1. és 2. példa alapján)
B. A sok roundtrip mindenképpen káros, emiatt lett lassú 2. is, mivel itt nem volt OR mapper overhead.
C. Az NHib elképesztően gyors volt (3. példa), csak 20%-kal lassabb volt, mint az Array Binding nyersen használva (2.2/2.7).
D. Az Array Binding eszement gyors, SQL Serveren a tábla típusú paraméterekkel lehet hasonlót elérni, de ehhez nem tudok OR mapper támogatást (mivel ehhez explicit kell fogadó kódot írni, az orához NEM).
E. A bulk copy mindenhol a leggyorsabb, de az oránál ez nagyon limitált, le is van írva, hogy arra gyúrnak, hogy az array binding legyen ugyanolyan gyors, így kiválthatja azt.

Azaz, ha megfelelően optimalizált az OR mapper adatelérése, akkor még olyan esetekben is használható, ahol első körben nem jutna az eszünkbe.

5. Két tábla között kell átmozgatni sorokat, egyszerű leképezések mentén. Sp, insert-select és delete, tranzakcióban.

6. Pessimistic Offline Lock pattern implementációban volt a következő. Meg kell nézni, létezik-e lock egy erőforrásra, ez egy egy szűrt select. Ha nem, akkor beszúrni egyet, ha igen, beszúrni egy várakozó sort. Mindezt serializable izolációs szinten, mivel meg kell akadályozni fantom sorok beszúrást a select és az insert között (orának is van ilyenje, bár nem lockol mint az Sql server alapban, hanem úgy működik, mint az RCSI az SQL servernél).
Érezhetően itt észnél kell lenni, mivel ha sokszor fut le a folyamat, akkor a serializable miatt lassú lehet a dolog. Design szempontból értelemszerűen az offline lockkal védeni kívánt dolgok lekérdezése NEM a serializable tranzakcióban volt. A védendő lekérdezés előbb lefut, majd utána jön a lock fogása, és ha sikerül, megkapja a kliens az eredményt, ha nem, akkor eldobjuk a lekérdezése eredményét. Ez pazarlás, ha ütközés van, de nem számítunk sok ütközésre. Ha egy tranzakcióban lenne a két feladat torlódás lenne, ha előbb a lock, aztán a művelet, akkor meg sikertelen művelet esetén (pl. secu beint) törölni kellene a lockot, de közben már lehet más vár rá…
Ezt a feladatot OR mapperrel írtam meg. Nem tudok pontos végrehajtási időt mondani, de ha jól emlékszem kb. 20 ms volt a teljes lock vizsgálat ideje. Ha serializable szinten egy erőforráson versengenének a folyamatok, akkor másodpercenként 50-nél többet nem tudna a szerver végrehajtani. Valójában azonban szórnak a lockolandó erőforrások, így a serializable nem fog jelentős torlódást okozni (megfelelő csak indexekkel persze).
Sokan szerintem ezt kapásból SP-ben írnák meg. Mit nyernénk vele? Mit akarunk minimalizálni? Az SQL szintű tranzakció idejét. A tranzakció kb. így nézne ki:
begin tran
select …
if () insert…
else másik insert…
commit

A kérdés, ezek ideje hogy aránylik a roundtripek által megnövelt időhöz? 3 roundtrip van, 1 select, 1 valamilyen insert és egy commit. Ha a roundtrip ideje mondjuk 3ms, akkor 9 ms a roundtrip overheadje. Ha az SQL műveletek kb. 10ms-ig tartottak, akkor kétszer olyan gyors lehet az sp-s megoldás. Azaz itt elgondolkodtató a dolog, de megint, én csak konkrét mérések alapján állnék neki átrefactorolni a megoldást spre, ilyen spekulatív úton nem. Hisz ha a roundtrip valójában csak 500 usec, akkor máris 1 napig felesleges dologgal múlattuk az időt.

7. Sok id alapján kell behozni pár száz sort. Lehetsége megoldás OR-mapper future queryvel, egy batchben, de ennek hátránya, hogy pl. SQL Servernél nem lehet 1-2 ezérnél több paramétert definiálni, így a generált sql nem lesz végrehajtható. Itt spt írtunk, amely oracle collection paraméterként vette át a kulcsok listáját, belül pedig joinoltunk az alaptáblához. SQL Servernél ezt tábla típusú paraméterrel csináltam volna meg. A bemenetet elegánsan át lehetett adni, mivel az NHibben lehet saját típust definiálni, így Guid tömb típus direkben átpasszolható volt az spbe.

Más. A már említett architektúrában volt sok olyan cross-cutting concern, amit db szinten implementálni véres lett volna. Például a WCF-ből kijövő objektum gráfot módosítani kellett, mivel sor (entitás) is mező (property) szintű ACL secu miatt meg kellett metszeni a kimenete fát (gráfot, mikor-mit), property-k értékét kimaszkolni, de közben feljegyezni a maszkoltakat, szöveget nyelvi fordítását beinjektálni a gráf tetszőleges helyére, tetszőleges entitást kiegészítő adatokat belerakni a fa megfelelő részére, be kellett küldeni minden tranzakció nyitása után egy user és tran azonosítót, offline lock információt, stb. Ezek nagyon komplex feladatok voltak, amelyeket ráadásul millisecundum nagyságrendű idő alatt kellett tudni megcsinálni tízezer elem feletti gráfokra is. Ezt én dbben nem tudtam volna megcsinálni, intenzív appszerver szintű cache-eléssel, illetve kihasználva a .NET adatstruktúráit sikerrel és elegánsan (központilag implementálva, mint az AOP-ban) megoldottuk.
Ha viszont ilyen mindent keresztülvágó követelményektől hemzseg a feladat, akkor ez ember kétszer is meggondolja, hogy kilépjen az entitások, a logikai modell világából, és elkezdjen spzni, mivel az spkre ráhúzni ezeket a megoldásokat nehézkes.
Szóval az OR mapper vs. sp kérdés igen komplex, nem lehet 2 perc alatt dönteni róla, és mindkettőnek megvan a helye, feladatonként egyesével kell eldönteni, melyiket használjuk.

2011.03.05.

Érdekes .NET perf tapasztalat

Filed under: .NET,.NET 4,C#,CLR,Optimalizálás,Szakmai élet,Visual Studio,VS 2008 — Soczó Zsolt @ 12:53

Amikor profilerrel megnézünk egy .NET kódot sokszor megdöbbentő helyen lesz benne bottleneck.

Az alábbi kód 1% időt visz el egy nagyon processzorintenzív kódban:

if (bar.L == 0)

Ami ebben lassú, az a System.Decimal.op_Implicit(int32). A bar.L egy decimal. Érdekes, mi?
Mi a megoldás? A 0 legyen valóban decimal, de int, amit konvertálni kell:

if (bar.L == 0M)

1% kevés, de sok 1% már számít.

2011.01.21.

Csúnya multithreading hiba

Filed under: .NET,.NET 4,C#,CLR,Szakmai élet — Soczó Zsolt @ 10:13

Double checked lockingnak indult, de bug lett belőle. Mit rontottam el?

private static volatile Dictionary> holidayDays;
private static readonly object staticLock = new object();

private Dictionary> GetHolidayDays()
{
if (holidayDays == null)
{
lock (staticLock)
{
if (holidayDays == null)
{
holidayDays = new Dictionary>();
FillTradingHours(holidayDays, “HOL”);
}
}
}
return holidayDays;
}

2010.11.26.

.NET Stringek lementése Memory Dumpból

Filed under: .NET,CLR,Debugging,Szakmai élet — Soczó Zsolt @ 11:02

Mostanában elég sokat turkálok problémás webappok dumpjában. Leírom magamnak is, hogy lehet lementeni .NET stringeket a dumpból.
WinDbg dolgok következnek.

A nagy stringeket kilistáztatom a
!DumpHeap -min 100000 -type System.String
paranccsal.

Valamelyik heapről kinézek magamnak egyet, és az előbbi parancs kimenetének első oszlopában található címet felhasználva belenézek a du cím paranccsal.
Pl.
0:000> du 5ce31140
5ce31140 “*****<sitecore database=”SqlSer”
5ce31180 “ver”>.. <sc.variable name=”da”

Az elején a kuszaság (átírtam csillagokra, mert elrontotta a htmlt) a .NET Stringek adminisztrációja és az object header.
Ha szemre tetszik a string, akkor pl. ki akarom menteni fájlba.
Nézzük meg a szöveges rész előtti bináris részt (dd).

0:000> dd 5ce31140
5ce31140 793308ec 00040001 0002836c 0073003c
Az első 8 byte az object header (32 biten, 64-en ez asszem 16 byte), sync block és type leíró. Számunkra ez érdektelen. A 3. dword már érdekesebb, ez a string hozza karakterekben. Azaz bájtban ennek a duplája, mert 2 bytes unicode ábrázolást használ a .net.
Így stringünk hossza 0002836c*2 byte. A 0073003c már a < karakter (003c) és az s betű (0073), szokásos fordított Intel sorrendben. A szöveges tartalom tehát a 5ce31140+0c címen kezdődik (headert átlépjük), és 0002836c*2 a hossza. A tartalom fájlba írása már egyszerű: .writemem c:\temp\s1.txt 5ce31140+0c L0002836c*2 Kedvencem a WinDbg, de ezt már mondtam. :)

2010.05.17.

Miért szeretem a dynamic típust reflection helyett?

Filed under: .NET,.NET 4,C#,CLR,Szakmai élet — Soczó Zsolt @ 12:21

Ezért:

var allEntities = (IEnumerable)reposType.GetMethod(“GetAll”, new Type[] { typeof(string[]) }).Invoke(repos, new object[] { includes });

vs.

var allEntities = (IEnumerable)repos.GetAll(includes);

Emellett a dynamic vagy 10x gyorsabb, még akkor is, ha a reflectionnél cachelem a típusleírókat.

2009.11.27.

Naív ojjektumhasználat – sok szemét

Filed under: .NET,C#,CLR,Szakmai élet — Soczó Zsolt @ 23:16

Két hete elkezdtem intenzíven használni a Visual Studio 2010-et, mivel szükségem volt a MemoryMappedFiles-ra a 4.0-s fwből. Ezt shared memoryként használom, amivel a laptopon kb. 1GByte/sec-kel tudok adatokat másolni két processz között, így kiváló cache-t tudtam építeni a segítségével. De most nem erről lesz szó.
A profilert is jelentősen továbbfejlesztették, van benne pl. rendes memória allokálás követés (lehet, hogy a régi is tudta ezt, akkor pardon).
Nézzük az alábbi képet:

A SecondInterval.Day property-t közel 6 milliószor hívtam meg, minden híváskor előállítva egy új ojjektumot, 140MByte-nyi szemetet hagyva magam után.

A naív implementáció így nézett ki:

public static SecondInterval Day
{
get { return new SecondInterval(86400); }
}

Maga a típus egy egyszerű Whole Value pattern implementáció, egy sima immutable objektum, ráadásul class, így referenciális típus.

Nem memóriapazarló módon így néz ki a property:

private static readonly SecondInterval aDay = new SecondInterval(86400);
public static SecondInterval Day
{
get { return aDay ; }
}

A readonly fontos, hogy nehogy valaki kiüsse a referenciámat, és berakjon egy sunyi másik időt reprezentáló ojjektumot a napi helyére. Az ojjektum immutable, ez fontos, másként semmit nem érne a readonly, a gyomrát lehetne piszkálgatni.
Amin még el lehetne gondolkodni, hogy struktúrává, value type-pá átalakítani a típust, csak akkor meg minden helyen ahol használom, másolni kellene az értékét. Mivel ez most belül egy 32 bites int, és 64 bit alatt futtatom, ahol a referenciák 64 bitesek, a másolással még mindig jobban járok. Úgyhogy lehet, hogy struct lesz, de előbb megnézem classként hogy muzsuikál.

2009.10.14.

Pár fejlettebb GC dolog

Filed under: .NET,C#,CLR,Szakmai élet — Soczó Zsolt @ 14:02

Ezeket érdemes megnézni, érdekesek.

Ha nem szeretnénk, hogy egy rövid, de teljesítményzabáló kódunkat megszakítsa a GC: Latency Modes és Low-Latency GC in .NET 3.5.

Ha interopolunk, és várunk visszahívást nem managed oldalról, de félünk, hogy a GC felszabadítja az ojjektumunkat visszahívás előtt: GC.KeepAlive.

Ha nagyon sok memóriát zabáló appot írunk, és nem szeretnénk belefutni OutOfMemoryException-be, inkább lassítanánk a feldolgozáson: MemoryFailPoint.

Garbage Collection Notifications in .NET 3.5 SP1.

Jó Garbage Collector cikkek

Filed under: .NET,C#,CLR,Szakmai élet — Soczó Zsolt @ 12:58

1, 2, 3, 4. Nem csak a szokásos, közismert dolgok, hanem mélyebbek is.
Pl. tudod-e, mekkora memóriát kér el induláskor a GC az OS-től? Hogy 3-féle GC is van (2-t ismer a legtöbb ember, már, aki egyáltalán hallott róla, hogy többféle GC is létezik)? Vagy miért száll el általában egy x86-os .net app 1.5G körüli memóriafelhasználásnál? Stb.

2009.08.04.

A jitter huncutságai

Filed under: .NET,C#,CLR,Szakmai élet — Soczó Zsolt @ 12:57

Érdekes és furcsa dolgok történnek néha a .net jitter miatt, ami nem nagyon fordul elő nem managelt környezetben.

Itt ez a kis factoryka részlet:

public IBroker GetBroker()
{
if (broker == null)
{
switch (BrokerMode)
{
case BrokerMode.Simulated:
broker = new BrokerSimulated();
break;
case BrokerMode.IB:
broker = IBBroker.Instance;
break;
}
}
return broker;
}

Amikor meghívtam ezt a sort:

BrokerFactory.Factory.GetBroker().SetInitialEquity(20000);

akkor furcsamód lefutott az élő broker, az IBBroker konstruktora. Pedig NEM az az ág futott le a switchben. Valószínűleg az történt, hogy a jitter hozzáért az IBBroker osztályhoz, ez pedig törvényszerűen kiváltotta a statikus konstruktor lefutását.

A megoldás pofonegyszerű (ha ismerjük az okot :), ki kell emelni a beteg típusra hivatkozást egy külön metódusba. Mivel a jitter metódusonként dolgozik, így addig tényleg nem nyúl a típusunkhoz, míg tényleg nem használjuk:

public IBroker GetBroker()
{
if (broker == null)
{
switch (BrokerMode)
{
case BrokerMode.Simulated:
broker = new BrokerSimulated();
break;
case BrokerMode.IB:
broker = CreateIBBroker();
break;
}
}
return broker;
}

private static IBBroker CreateIBBroker()
{
return IBBroker.Instance;
}

2009.02.12.

.NET teljesítményhangolási tapasztalatok 5.

Szeretjük a connection poolt, de nem mindig.

Tudjuk, hogy manapság nem illik sokáig nyitva tartani az adatbázis kapcsolatot, hanem bemegyünk, kihozzuk a szükséges adatokat, majd lezárjuk a kapcsolatot. Régen (n > 10 év) ez nem volt okos dolog, mert lassú volt a belépés-kilépés. Ezért találták ki a Connection Poolt, amit OLEDB-ben még Session Poolnak hívtak. Egykutya.
Ez arról szól, hogy a kliens oldali adatelérő komponens, pl. az ADO.NET SqlClient providere (System.Data.SqlClient névtér és kölkei) újrahasznosítja a lezárt kapcsolatot. Azaz mi SqlConnection.Close-t mondunk, ő viszont a háttérben nem zárja le a kapcsolatot az adatbázis felé. Ha ezek után újra meg akarjuk nyitni egy kapcsolatot az adatbázis felé, nem hoz létre új kapcsolatot a provider, hanem visszaad egy használtat a poolból. Miért jó ez? Nyilván egy tényleges kapcsolat kiépítése igen költséges, TCP csatorna vagy Named Pipe session felépítése, autentikáció, ecetera. Ezt nagyon sokszor megússzuk, hála a poolnak. A pool 4-8 perc közötti véletlenszerű időközönként azért lezárogatja a használt kapcsolatokat, ott ne zápuljanak.
Nyilvánvaló, hogy felhasználónként egyedi poolok vannak, így egy sysadmin által eldobott kapcsolatot nem adja oda nekem, lópicinek a provider, ez csúnya luk lenne.
Lehet azért így is aljaskodni az új bérlőkkel. Mi van, ha becsatlakozok, kiadok egy use másik adatbázist, majd röhögve lezárom a kapcsolatot? És ha egy-ket set opciót átírok, pl. SET LANGUAGE urdi? Mit lát a következő hívó, aki megkapja a használt kapcsolatot? Hát amit beállítottunk az előző lépésben. Azért ez durván hangzik, ugye? Szerencsére a helyzet az, hogy alapban tiszta környezetet kapunk, köszönhetően annak, hogy a provider pool manager-e végrehajt egy sp_reset_connection hívást mielőtt visszaadná nekünk a használt kapcsolatot. Hogy ez pontosan mit csinál, azt egyszer már leírtam, tessék megnézni a listát.
Azaz bár nem kell minden egyes kéréshez új kapcsolatot megnyitni, azért egy sp hívás csak történik pluszban a háttérben. SQL Profilerben látszik, hogy ez nagyon gyors, de azért ez mégis csak egy hálózati körülfordulás.
És most jön a trükk. Ha teljesen biztosak vagyunk benne, hogy nem tolunk ki a következő hívóval aki megkapja a használt kapcsolatunkat, akkor kikapcsolhatjuk a takarítást. A connection stringbe ezt kell beírni:

Connection Reset=false;

Nézzük meg az alábbi kódot:

SqlConnection conn = new SqlConnection(“data source=.;initial catalog=ATS;Integrated security=true;”);
SqlCommand cmd = new SqlCommand(“sp_who”, conn);
cmd.CommandType = CommandType.StoredProcedure;

for (int i = 0; i < 3; i++) { conn.Open(); cmd.ExecuteNonQuery(); conn.Close(); } [/source] Az SQL Profilerben ez látszik: [source='C'] EventClass TextData SPID Audit Login -- network protocol: LPC RPC:Completed exec sp_who 52 Audit Logout 52 RPC:Completed exec sp_reset_connection 52 Audit Login -- network protocol: LPC RPC:Completed exec sp_who 52 Audit Logout 52 RPC:Completed exec sp_reset_connection 52 Audit Login -- network protocol: LPC RPC:Completed exec sp_who 52 Audit Logout 52 [/source] Hoppá, ez nem az, amit vártunk! Csak 1 logint az elején és 1 logoutot a végén, közben meg az sp_resetconnectiont és az sp_who-nkat. Nem működik a pooling? De. Csibész a profiler. "The Audit Login event class indicates that a user has successfully logged in to Microsoft SQL Server. Events in this class are fired by new connections or by connections that are reused from a connection pool." Ha bekapcsoljuk a profilerben az Event Subclasst, kicsit tisztul a kép: [source='C'] EventClass TextData SPID EventSubClass Audit Login -- network protocol: LPC 1 - Nonpooled RPC:Completed exec sp_who 52 Audit Logout 52 2 - Pooled RPC:Completed exec sp_reset_connection 52 Audit Login -- network protocol: LPC 2 - Pooled RPC:Completed exec sp_who 52 Audit Logout 52 2 - Pooled RPC:Completed exec sp_reset_connection 52 Audit Login -- network protocol: LPC 2 - Pooled RPC:Completed exec sp_who 52 Audit Logout 52 1 - Nonpooled [/source] Szóval van pooling, csak a profiler ravaszkodik. Már kivert a víz. Viszont valami ebben zavar. A pooling ugye a kliensen van. Honnan tudja a szerver egy új parancs beérkeztekor az egy új parancsnak számít a kliens részéről a még nyitott kapcsolaton, vagy csak a poolból visszakapott kapcsolaton hajtanak végre egy új parancsot? Szerintem ő mesterségesen szintetizálja a poolos login-logoutokat, az sp_reset_connection-ök beérkeztekor veszi őket körbe login-logout párossal, a feeling kedvéért. De ez csak tipp. Kapcsoljuk ki a connection resetet: [source='C#'] SqlConnection conn = new SqlConnection( "data source=.;initial catalog=ATS;Integrated security=true;connection reset=false;"); [/source] Mit látunk a profilerben? Semmi nem változott! .NET fw. 2.0 felett nem lehet kikapcsolni az sp_reset_connection-t! Ezért nincs már a legújabb doksiban benne ez a beállítás.

Foly. köv.

2009.01.26.

.NET fw. platform invoke 64 biten

Filed under: .NET,CLR,Szakmai élet — Soczó Zsolt @ 14:29

Egy apró érdekesség. Van egy 32 bites windows dll, amit a gyártó még nem írt meg 64 bitre. Ezt .NET-ből a jól ismert DllImport attribútummal, PInvoke segítségével lehet meghívni. 64 bites gépen azonban alapértelmezetten a C# fordító olyan assemblyket generál, amelyek platform függetlenek, így 64 bites osen a jitter 64 bites gépikódot generál. Persze, így már nem tudja betölteni a 32 bites DLL-t a pinvoke, és elszáll a program hibával, amikor először hívnánk egy függvényt a DLL-ből. A megoldás egyszerű: meg kell jelölni a projektet, hogy x86-ra forduljon. Ettől az IL kód persze nem változik, de a metadatok között ott lesz a jelzés a jitternek, hogy tessék 32 bites kódot generálni.
A bosszantó ebben az esetben csak az, hogy azért lett volna jó a natív 64 bites memóriamodell, mert tényleg gigabájtnyi adatot akartam a memóriában kezelni.

2008.12.11.

Debugolás a .NET fw. forrása segítségével

Filed under: .NET,C#,CLR,Debugging,Szakmai élet,Visual Studio,VS 2008 — Soczó Zsolt @ 12:32

Kaptam egy igen nehezen megközelíthető problémát, amelyben a fókusz a TAB-ra átlépett egyes controlokat. Nem egy triviális TabStop=false probléma volt.
VS 2005-ös projektekről van szó, átkonvertáltam őket 2008-ra, hogy tudjam a fw. forráskódját is debugolni. Az _NT_SYMBOL_PATH= nekem be van állítva a gépen a publikus os szimbólumokra (elsősorban ahhoz amikor WinDbgozok), emiatt nem tudtam a vsből .net fw.-öt debugolni, mert előbb lehúzza a stripped szimbólumokat, a teljeshez már hozzá se nyúl. Ezért egy bat-ból indítom a vs-t, előtte kiütve az eredeti _NT_SYMBOL_PATH-t.
Így már ment a fw. forrás debug, de mivel a clr az ngenelt optimalizált kódot töltötte be, ezért nagyon sok típus belseje nem látszik normálisan. Erre megoldás itt található. Le lehet tiltani, hogy a CLR az ngenelt kódot töltse be, így már rendesen lehet debugolni.
Lehetne, ha nem lenne elcsúszva némelyik forráskód a pdb-ben található sorszámoktól. Ilyenkor van az, hogy teljesen más sorokon lépkedünk végig, mint amit a source ablakban látunk, pl. kommenteken lépked végig a debugger.
A megoldás erre egyszerűbb volt, mint gondoltam volna: próbaképpen kitöröltem 3 sort pl. a Control.cs elejéből, így visszaállt a szinkron.
Maga az alapprobléma egyébként abból adódott, hogy egy kompozit Third Party Contol explicit letiltotta
a TAB-olást, a ControlStyle-ból kivéve a Selectable flaget.

2008.07.31.

Uninstalling .NET Framework 2.0 SP2 and 3.5 SP1 in Vista

Filed under: .NET,C++/CLI,CLR,Szakmai élet — Soczó Zsolt @ 23:23

Recently I had a run-time heap corruption problem in our software which is caused by some internal changes in the beta CLR and .NET Framework assemblies. This software uses lots of interop code, it become broken because of these changes in the fw.
So, I decided to uninstall the beta frameworks from my machine but it’s happened to be not such a trivial thing to do. I asked Aaron Stebner in Microsoft for some help who responded me kindly and promptly. Thanks for it. :)

Here is his answer:

“The original version of the .NET Framework 3.5 included the .NET Framework 2.0 SP1 and 3.0 SP1, but it created separate Add/Remove Programs entries for 2.0 SP1 and 3.0 SP1 so you could uninstall all of the pieces as long as you uninstalled in the exact order of 3.5, 3.0 SP1, 2.0 SP1. That same behavior should be the case for 3.5 SP1 as well.

One thing that might be different here – on Windows Vista and Windows Server 2008, the .NET Framework 2.0 and 3.0 are OS components, so they will not show up in Add/Remove Programs. If you are running one of these OS’s, you will need to use steps like this to remove the .NET Framework 2.0 SP2 and 3.0 SP2:

1. Uninstall the .NET Framework 3.5 SP1 product from the Programs and Features control panel
2. In the Programs and Features control panel, click the link on the left named View Installed Updates
3. In the list of installed updates, look for an item named Update for Microsoft Windows (KB948609) – this is the .NET Framework 3.0 SP2
4. Right-click on the item and choose Uninstall
5. In the list of installed updates, look for an item named Update for Microsoft Windows (KB948610) – this is the .NET Framework 2.0 SP2
6. Right-click on the item and choose Uninstall
7. Reboot”

So, the key to uninstall these beta bits from your Vista machine is to remove KB948609 and KB948610 hotfixes.

Apparently the fw. rollback healed our application. We will investigate this problem if it comes up in the RTM bits too (in few weeks I think). I hope it won’t. :)

Bocs, de ezt Aaron kérésére angolul írtam.
A lényeg, hogy ha Vistán le kell szedni az SQL 2008 RC0 vagy más által felrakott beta NET Framework 2.0 SP2-t és 3.5 SP1-et, akkor a KB948609 és KB948610 hotfixeket kell uninstallálni.

2008.01.17.

A nap híre: .NET Framework Library Source Code now available

Filed under: .NET,CLR,Debugging,Szakmai élet — Soczó Zsolt @ 13:54

Ma mindenkinél erről lehet majd hallani, örülünk, böngészünk, debugolunk.

2007.08.25.

Halmaz osztály a .NET Fw-ben

Filed under: .NET,CLR,Linq,Szakmai élet — Soczó Zsolt @ 16:26

Nem új a dolog, de a LINQ kapcsán láttam meg a reflectorban ezt a jószágot: HashSet.

Nagyon örülünk, ez hiányzott a fwből. A fákra viszont kicsit még rámászhatnának, mert azok továbbra is hiányoznak…

2007.07.25.

Rossz sorrendű inicializálás .NET-ben?

Filed under: .NET,CLR,Szakmai élet — Soczó Zsolt @ 08:54

Javaban van egy közismert jelenség, ami miatt a Double Checked Locking nem működik jól, mégha az ötlet zseniális is.
Régóta érdekelt, hogy vajon ugyanez a probléma fennáll-e .NETben is? Belső infó szerint nem, legalábbis az MS CLR-ben nem.
Érdemes elolvasni a probléma okát is az előző cikkben, rövid.

2006.10.26.

StringBuilder vs. String

Filed under: .NET,CLR,Szakmai élet — Soczó Zsolt @ 17:18

Sokan olvasták, hogy a StringBuilder a nyerő, ha több stringgel kell dolgozni, pl. összefűzni őket. Aztán itt meg is állnak, és már pár string esetén is boldogan izzítják a StringBuildert, és büszkék, hogy ez aztán a szakos kód.

De biztos így van ez?

Nem.

Meglepő módon a pl. a string.Join jóval gyorsabb, mint a StringBuilder.Append? Hogy mi? Sok elemre is? Igen.

Meglepőek néhol az eredmények, érdemes megnézni a cikket.

A szerzőnek abban is igaza van, hogy nagy stringeknél (több k vagy nagyobb) nagyon jó a StringBuilder, de kicsiknél, amik sokkal tipikusabbak, nem feltétlenül.

2006.07.03.

Az elmúlt hét tanulságai

Filed under: .NET,C#,C++/CLI,CLR,Szakmai élet — Soczó Zsolt @ 16:06

Hajmeresztő (szerencsére csak szakmailag) időszakon vagyok lassan túl, ezért is nem blogoltam már régen.

Pár vegyes tanulság az elmúlt időszakból, zöme magamnak is emlékeztető:

  • A C/C++ secure stringkezelő (és egyéb) függvények nem úri huncutságok, és nem csak security szempontjából fontosak. A buffer overrun fogalom sok embernek a hekkeléssel forrt össze, pedig adatvesztések, instabilitások is gyakran következményei. Ha egyszer vannak rá normális függvények, és a kódok átírása se olyen vészes (tudom, mert átírtam vagy 100 függvényhívást az új függvényekre), akkor nem látok okot nem megtenni. Ahol a régi kód kussol, csendben elbassza a dolgokat az új visong. Ugyanez a helyzet a /GS kapcsolóval is (stack overrun ellenőrzés), valamint a /analyze opcióval (prefast) is. Igenis hallgatni kell rájuk, mert értelmes hibákat szúrnak ki.
  • Ha az IIS alatt nem megy egy website, Service Unavailable hibával nem indul a worker process, és 0x80004005-ös (Access Denied) hiba van az eventlogban, akkor az iis valahol nem tud írni vagy olvasni. Jelentős FileMon/Regmon erőfeszítéseim ellenére se jöttem rá mi hiányzik neki, de az aspnet_regiis -ga megadja a szükséges jogokat a futtatáshoz.
  • Ha egy osztály IDisposable, akkor kurvára meg kell hívni a Dispose metódust. Nem tudok elég nagy betűket rendelni ehhez:

Használd a C# usingot, bmeg!!!

Én a DirectoryServicessel szívtam meg, mivel az nemmanazsolt kódot és erőforrásokat hívokat a háttérben úgy fejreáll mint állat pár száz allokált objektum után, ha nem Disposolunk keményen.

  • A backup hasznos dolog. Ennek hiányában a GetDataBack-kel számottevő sikereket érhetünk el. A rendszergazda meg barátkozzon össze a kapa nyelével a megfelelő testrészén.

Powered by WordPress