יום ראשון, 18 באוגוסט 2013

Tracing High Performance Applications - Event Tracing for Windows - ETW

ביצוע instrumentation אפליקטיבי הוא חלק הכרחי במערכות תוכנה סבוכות. כתיבה עשירה של tracing ו logging מאפשר לנו לעקוב אחר ההתנהגות של האפליקציה שלנו - במיוחד בסביבות פרודקשין שבו אנחנו לא יכולים להתחיל לדאבג את המערכות.
זה מתחיל להיות קריטי עבור מערכות High-performance (למשל אפליקציות BIZTALK)שבהם כל כתיבה ללוג או ל Trace יכול להפחית את ביצועי המערכת.

האופציות השכיחות לכתוב ל Trace (באמצעות קומפוננטות צד שלישי):

  1. System.Diagnostic.Trace.Component
  2. Enterprise Library Tracing Component
  3. Log4Net
אבל:
  • כאשר נפתח את DebugView כדי לצפות בEvent-ים הרבים , הכלי יצרוך כ 85% ממשאבי ה CPU. (הכלי  DebugView מתפקד  כ Debugger  ונרשם ל OUTPUT_DEBUG_STRING_EVENT) כתוצאה מכך יש פגיעה משמעותי ב Performance של האפליקציה. אם מדברים על אפליקציית Biztalk, יתכן מצב שכתוצאה מכך יכנסו לנו תהליכים למצב של Suspended.
  • ביצועים נמוכים-System.Diagnostic.Trace.Component יודע לכתוב כ 2000-8000 כתיבות בשניה (כאשר DebugView Event Capture במצב Enabled)
  • כל שינוי ב tracing configuration דורש אתחול של האפליקציה (לרוב..). בפרויקט ביזטוק נדרש לאתחל את ה Host Instance- לפעמים זה לא מתאפשר בסביבת ה Prod.
לכן, הפתרון המומלץ:

Event Tracing For Windows-ETW- תשתית לוגים סקאלאבילית שמערכת ההפעלה מספקת לנו (מאז שנת 2000). החסרון זה שלא קל לעבוד מול התשתית הזאת, אך יש המון דוגמאות באינטרנט כיצד לעבוד מול התשתית.

אז מה התשתית מספקת לנו:
  • בניסוי שנערך על מחשב עם 4 ליבות, התשתית כתבה כ 1.4 מיליון!!! של Trace event לשניה!
  • כל שינוי ב tracing configuration לא דורש אתחול של האפליקציה
  • צריכה נמוכה של משאבי CPU- התשתית משתמשת בפתרון Buffering שממומש על גבי הKernel, שכותב באופן א-סינכרוני לדיסק ע"י Thread נפרד.
להלן בדיקות הביצועים שנעשו על גבי כל 4 התשתיות שציינתי:

הבדיקה נעשתה ע"י הרצה של מטודה שכותבת ל Trace רשומה אחת, ואותה הריצו בלולאה 100,000 פעמים. בסיום ההרצה התשתיות היו צריכות לספק קובץ לוג מבוסס Text


אפשר לראות בבירור את השימוש ב ETW עבור מערכות High-Performance.


תהנו!

יום חמישי, 1 באוגוסט 2013

בדיקות אוטומאטיות באמצעות BizUnit

אצלי בצוות כבר עובדים המון זמן עם Biztalk. יש אפליקציות Biztalk שקיימות כבר כמה שנים. לאחר כל שינוי באפליקציה מבצעים בדיקות כמובן. לרוב אלו בדיקות אינטגרציה עם מערכות אחרות.
עד היום, הבדיקות בוצעו באופן ידני (למשל: לוקחים קובץ לדוגמא, זורקים לתיקייה, ובודקים לאן הקובץ נשלח, ואת התוכן שלו). תתארו כמה זמן הטסט הידני הזה לוקח..

לאחרונה התחלנו להשתמש עם ה BizUnit ככלי בדיקות אוטומאטי עבור כל בדיקות האינטגרציה.
כמובן שלוקח קצת זמן ללמוד להשתמש, ולהתחיל לכתוב טסטים מקצה לקצה, אך לאורך זמן המאמץ ישתלם.
BizUnit זוהי פלטפורמה לבדיקות דקלרטיבית חינמית לבדיקות Black-Box אשר מיועדת בעיקר לבדיקות אפליקציות ה Biztalk, אך כמובן אפשר להשתמש בכלי עבור כל אפליקציה שאנחנו יוצרים ( תהיו יצירתיים..).

הסבר להורדה, התקנה, ודוגמאות אפשר להוריד מכאן.
את מגוון הבדיקות שאפשר לעשות עם הפלטפורמה אפשר למצוא כאן

את הבדיקות אפשר לכתוב כקובץ XML, או לכתוב כקוד #C. היתרון הגדול שאפשר להשתמש ולמחזר קומפוננטות בדיקה שונות שכתבתם במהירות, עבור טסטים שונים.

בהתחלה נתקלתי במספר קשיים בכתיבת הבדיקות, אך לאחר מחקר קטן באינטרנט אפשר היה למצוא פתרון.
אני אציג סנריו קצר לבדיקות באמצעות הפלטפורמה, שמהווה (גם לי) בסיס ליצירת בדיקות חדשות.

את כל ה step נכתוב כמובן בתוך פונקציית בדיקה רגילה- [TestMethod]

שלב 1-נגדיר TestCase שהוא בעצם מייצג את הסנריו שלנו:

public class CarShop
 var totalItemsTest = new TestCase { Name = "my test case" };
 string dbConnectionString = @"Data...";

שלב 2- מחיקת כל הקבצים (במקרה שלנו קבצי XML) מהנתיב שבו נוצר הקובץ שאנחנו רוצים לבדוק (למשל לאחר ה send-port)


DeleteStep deleteStep = new DeleteStep();
            deleteStep.FilePathsToDelete = new Collection<string> { @"c:\out\*.xml" };
            totalItemsTest.SetupSteps.Add(deleteStep);

שלב 3- מחיקת רשומות מה DB שלנו (במקרה הזה SQL SERVER)


     var dbTestStep = new BizUnit.TestSteps.Sql.DbQueryStep();
            dbTestStep.ConnectionString = dbConnectionString;

            dbTestStep.SQLQuery = new SqlQuery
            {
                RawSqlQuery = @"Delete From SomeDb.dbo.table; select 1;"
            };

            dbTestStep.NumberOfRowsExpected = 1;
            dbTestStep.DelayBeforeCheck = 10;

            totalItemsTest.ExecutionSteps.Add(dbTestStep);


שלב 4- בעצם כאן מתחילים את הבדיקה עצמה- העברת קובץ מתיקיית מקור ליעד ( לתיקייה שהביזטוק יקרא ממנה)
     
            var moveTeststep = new CreateStep();
            moveTeststep.CreationPath = @"c:\destination";

            var dataLoader = new FileDataLoader();

            dataLoader.FilePath = @"c:\fromLocation";
            moveTeststep.DataSource = dataLoader;

            totalItemsTest.ExecutionSteps.Add(moveTeststep);

שלב 5- כעת הקובץ עבר לתיקייה, והביזטוק (או המערכת) תקרא את הקובץ, נמתין זמן מסויים (עד שהמערכת שלנו "תעבד" את הבקשה לפני שנמשיך עם שאר הבדיקות:

var waitTestStep = new DelayStep();
            waitTestStep.DelayMilliSeconds = 10000;
            totalItemsTest.ExecutionSteps.Add(waitTestStep);


שלב 6- נתשאל רשומה ב DB ונבדוק אם קיבלנו מה שציפינו לקבל:

     var dbSelectTestStep = new DbQueryStep();

            dbSelectTestStep.ConnectionString = dbConnectionString;
            dbSelectTestStep.SQLQuery = new SqlQuery
            {
                RawSqlQuery = @"select top(1) from tbl where cell='11' "
            };

            dbSelectTestStep.NumberOfRowsExpected = 1;
            dbSelectTestStep.DbRowsToValidate = new Collection();
            DbRowToValidate row = new DbRowToValidate();
            DbCellToValidate cell = new DbCellToValidate();
            cell.ColumnName = "colName";
            cell.ExpectedValue = "12312";
            row.Cells.Add(cell);
            dbSelectTestStep.DbRowsToValidate.Add(row);
            totalItemsTest.ExecutionSteps.Add(dbSelectTestStep);

שלב 7- נבדוק שקיבלנו קובץ XML אחד בתיקיית ה OUTPUT שלנו, ושהוא בנוי על פי סכמה מסויימת:


      var validatingFileStep = new FileReadMultipleStep()
            {
                DirectoryPath=@"c:\out",
                SearchPattern="*.xml",
                Timeout=1000,
                ExpectedNumberOfFiles=1
            };

            var validation = new XmlValidationStep();
            var schemaSummary = new SchemaDefinition
            {
                XmlSchemaPath=@"c:\schema.xsd",
                XmlSchemaNameSpace="http://Schemas/namespace"
            };

            validation.XmlSchemas.Add(schemaSummary);

שלב 8- נבדוק שקיבלנו ב XML ערכים כפי שציפינו - על ידי XPATH


      var xpathStep = new XPathDefinition() 
            {
            
            Description="type",
            XPath="/*[local-name().....",
            Value="expected value"
            };



            validatingFileStep.SubSteps.Add(validation);
totalItemsTest.ExecutionSteps.Add(validatingFileStep);

ובשלב האחרון פשוט מריצים את כל ה STEP'S:

       var bizUnit = new BizUnit.BizUnit(totalItemsTest);
            bizUnit.RunTest();

כעת כל ה STEP ירוצו אחד אחרי השני.
אם אחד ה Step ים יכשל, יופיע Exception מסודר, שמפרט בדיוק באיזה Step הבדיקה נכשלה.

בדיקות מהנות!