اهمیت code review
اگر کدی آزمایش شده، مرور شده و "کار میکنه"، دیگه نیازی به تغییر اون وجود نداره. برنامه نویس ها عموما در دوران حرفه ای خودشون، حداقل یک بار با "وسوسه بازنویسی همه چیز از نو" روبرو میشن، وسوسه ای که در ابتدا، افق های روشنی رو برامون ترسیم میکنه، اما در انتها، منجر به داشتن کدی به مراتب بدتر از اون چیزی که در ابتدا داشتیم، میشه.
وقتی کدی قدیمی (که بدون مشکل کار میکنه) رو دور میندازیم، در حقیقت داریم زمانی رو که صرف رفع ایرادهای موجود در اون کرده بودیم (که میتونه روزها، هفته ها یا ماه ها باشه) رو هدر میدیم. گذشته از این، چون احتمالا به تمام بخش های کد و عملکرد اون اشراف نداریم، چیزهایی ممکنه در کد ببینیم که به نظرمون احمقانه بیاد و حذف اونها، باز موجب از کار افتادن بخش هایی از سیستم بشه که Debug کردن اون، مستلزم صرف زمانی هستش که تیم قبلی اون زمان رو یکبار صرف این کار کرده بوده. بنابراین، نمی تونیم به عنوان یه اصل کلی عبارت "کدی که کار میکنه رو نباید تغییر داد" رو رد کنیم! این مساله، باید بازای Case های مختلف، بدقت بررسی بشه و بعد در مورد اون Case خاص، نظر داده بشه.
طبیعتا، با دیدن کد آورده شده در این پست میشه به این مساله پی برد که نویسنده اون کد، در وهله اول، با اصول و مفاهیم اولیه نوشتن یک کد تمیز، بیگانه بوده. چنین افرادی، ابتدا باید آموزش ببینن و مرور یا عدم مرور کد اونها، در طولانی مدت، هیچ سودی در پی نخواهد داشت.
موفق باشید.
مقدمه
در لینکی که چندی پیش به اشتراک گذاشته بودم؛ به مطلبی تحت این عنوان اشاره شده بود: "آیا از KPI باید به انباره داده و هوش تجاری رسید؟" (بر گرفته از وبلاگ آقای جام سحر) که در آن به موانع پیش روی انجام پروژههای BI در ایران پرداخته شده است.این مقاله بر گرفته از فصل سوم یکی از White Paperهای ماکروسافت با عنوان Microsoft EDW Architecture, Guidance and Deployment Best Practices میباشد. که به شرح عملیات Loading در فاز ETL میپردازد. از آنجا که به منظور پیاده سازی این نوع پروژهها معمولاً در ایران برون سپاری صورت میگیرد و مدیران شرکتها بیشتر درگیر سیستمهای OLTP هستند و مجری پروژه (شرکت پیمانکار) معمولاً کوتاهترین مسیر را جهت انجام پروژه انتخاب میکند(و امروزه نیک میدانیم که "انتخاب مسیرهای کوتاه در زمان کم میتواند به پیچیدگیهای بسیار جدی در دراز مدت منجر شود!") و همچنین از آنجا که متاسفانه به دلیل عدم ثبات مدیریت در ایران معمولاً "مدیریت برای تحویل پروژه تحت فشار است و نه برای مسائل پشتیبانی " و مسائل دیگری از این دست؛ چنانچه در تحویل گیری محصول به درستی تست نرم افزار صورت نگیرد، در نظر گرفتن موارد زیر:
Validation: Are we building the right product? ~ Software is traceable to customer requirements
2- Detecting Net Changes
2-1- Pulling Net Changes – Last Change Column
2-2- Pulling Net Changes – No Last Change Column
2-3- Pushing Net Changes
3- ETL Patterns
3-1- Destination load Patterns
3-2- Versioned Insert Pattern
3-3- Update Pattern
3-4- Versioned Insert: Net Changes
4- Data Integration Best Practices
4-1- Basic Data Flow Patterns
4-1-1- Update Pattern
4-1-2- Update Pattern – ETL Framework
4-1-3- Versioned Insert Pattern
4-1-4- Update vs. Versioned Insert
4-2- Dimension Patterns
4-3- Fact Table Patterns
4-3-1- Managing Inferred Members
1- Full Load vs Incremental Load
نسلهای اولیه DW (اختصار Data Warehouse) به شکل Full Loads پیاده سازی میشدند، به این طریق که هر بار عملیات بارگذاری صورت میگرفت، DW از نو دوباره ساخته میشد. شکل زیر مراحل مختلف انجام شده در این روش را نمایش میدهد:
پروسه Full Load شامل مراحل زیر بود:
- Drop Indexes: از آنجا که Indexها زمان بارگذاری را افزایش میدادند، این عمل صورت میپذیرفت.
- Truncate Tables: تمامی رکوردهای موجود در جداول حذف میشدند.
- Bulk Copy
- Load Data
- Post Process: شامل عملیاتی نظیر شاخص گذاری روی داده هایی است که اخیراً بارگذاری شده اند و....
روی هم رفته Full Load مسئله ای مشکل ساز بود، زیرا نیاز به زمانی برای بارگذاری مجدد دادهها داشت و مسئلهی مهمتر نداشتن امکان دستیابی به گزارشاتی تاریخچه ای با ماهیت زمان برای مشتریان کسب وکار بود. به این دلیل که همواره یک کپی از آخرین دادههای موجود در سیستم عملیاتی درون DW قرار میگرفت؛ که با بکارگیری Full Load اغلب قادر به ارائهی این نوع از گزارشات نبودیم، بدین ترتیب سازمانها به نسل دوم روی آورند که در این دیدگاه از مفهوم Incremental Load استفاده میشود. اشکال زیر مراحلی که در این روش انجام میشود را نمایان میسازد:
Incremental Load with an Extract In area
Incremental Load without an Extract In area
مراحل Incremental Load شامل:
- بارگذاری تغییرات نسبت به آخرین فرآیند بارگذاری انجام شده
- درج / بروزرسانی تغییرات درون Production area
- درج / بروزرسانی Consumption area نسبت به Production area
تفاوتهای اصلی میان Full Load و Incremental Load در این است که در Incremental Load:
- نیازی به پردازشهای اضافی جهت حذف شاخص ها، پاک کردن تمامی رکوردهای جداول و ساخت مجدد شاخصها نیست.
- البته نیاز به رویه ای جهت شناسایی تغییرات میباشد.
- و همچنین نیاز به بروزرسانی بعلاوه درج رکوردهای جدید نیز میباشد.
ترکیب این عوامل برای ساخت Incremental Load کارآمد تر، منجر به پیچیدهتر شدن پیاده سازی و نگهداری آن نیز میشود.
2- Detecting Net Changes
فرآیند لود افزایشی ETL، بایست قادر به شناسائی رکوردهای تغییریافته در مبداء باشد، که این عمل با استفاده از هر یک از تکنیکهای Push یا Pull انجام میشود.
- در تکنیک Pull، فرآیند ETL رکوردهای تغییریافته در مبداء را انتخاب میکند:
- ایدهآل وجود داشتن یک ستون Last Changed در سیستم مبداء است؛ که از آن میتوان جهت انتخاب رکوردهای تغییر یافته استفاده نمود.
- چنانچه ستون Last Changed وجود نداشته باشد، تمامی رکوردهای مبداء باید با رکوردهای مقصد مقایسه شود.
- در تکنیک Push، مبداء تغییرات را شناسائی میکند و آنها را به سمت مقصد Push میکند؛ این درخواست میتواند توسط فرآیند ETL انجام شود.
2-1- Pulling Net Changes – Last Change Column
بیشتر جداول در سیستمهای مبداء حاوی ستون هایی هستند که زمان ایجاد و یا اصلاح رکوردها را ثبت میکنند. در نوع دیگری از سیستمهای مبداء ستونی با مقدار عددی وجود دارد، که هر زمان رکوردی تغییر یافت به آن ستون مقداری اضافه میشود. هر دوی این تکنیکها به فرآیند ETL اجازه میدهند، بطور کارآمدی رکوردهای تغییریافته را انتخاب کند. (با مقایسه، بیشترین مقدار قرار گرفته در آن ستون؛ که در طول آخرین اجرای فرآیند ETL بدست آمده است). نمونه ای از جداول سیستم مبداء که دارای تغییرات زمانی است در شکل زیر نمایش داده میشود.همچنین شکل زیر نشان میدهد، چگونه یک مقدار عددی میتواند به منظور انتخاب رکوردهای تغییریافته استفاده شود.
2-2- Pulling Net Changes – No Last Change Column
شکل زیر گردش فرآیند را هنگامی که ستون Last Change وجود ندارد؛ نمایش میدهد.این گردش فرآیند شامل:
- Join میان مبداء و مقصد با استفاده از یک دستور Left Outer Join است.
- تمامی رکوردهای مبداء که در مقصد وجود ندارند، پردازش میشوند.
- زمانی که رکوردی در مقصد وجود داشته باشد مقادیر دادههای مبداء و مقصد مقایسه میشوند.
- تمامی رکوردهای مبداء که تغییر یافته اند پردازش میشوند.
2-3- Pushing Net Changes
دو متد متداول Push وجود دارد که در تصویر زیر نمایش داده شده است.
تفاوت این دو روش به شرح زیر است:
- در سناریو اول (شکل سمت چپ)؛ بانک اطلاعاتی رابطه ای سیستم مبداء Transaction Log را مرتب مانیتور میکند تا تغییرات را شناسائی کرده و در ادامه تمامی این تغییرات را در جدولی در مقصد درج میکند.
- در سناریو دوم؛ توسعه دهندگان Trigger هایی ایجاد میکنند تا هر زمان که رکوردی تغییر یافت، تغییرات در جدولی که در مقصد وجود دارد درج گردد.
مسئله ای که در هر دو مورد وجود دارد Load اضافه ای است؛ که روی سیستم مبداء وجود دارد و میتواند Performance سیستمهای OLTP را تحت تاثیر قرار دهد. به هر روی سناریو نخست معمولاً کاراتر از سناریویی است که از Trigger استفاده میکند.
3- ETL Patterns
پس از شناسائی رکوردهایی که در مبداء تغییر یافته اند، نیاز داریم تا این تغییرات در مقصد اعمال شود. در این قسمت به معرفی الگوهایی که برای اعمال این تغییرات وجود دارد میپردازیم.
3-1- Destination load Patterns
تشخیص چگونگی اضافه نمودن تغییرات در مقصد تابع دو عامل زیر است:
- آیا رکورد هم اینک در مقصد وجود دارد؟
- الگوی استفاده شده برای جدول مقصد به کدام شکل است؟ (Update یا Versioned Insert)
فلوچارت زیر نشان میدهد، به چه شکل جداول مقصد متاثر از چگونگی پردازش رکوردهای مبداء قرار دارند. توجه داشته باشید که عمل بررسی بطور جداگانه و در یک لحظه صورت میگیرد.
3-2- Versioned Insert Pattern
Kimball Type II Slowly Changing Dimension نمونه ای از الگوی Versioned Insert است؛ که در آن نمونه ای از یک موجودیت دارای ورژنهای متعددی است. مطابق تصویر زیر؛ این الگو به ستونهای اضافه ای نیاز دارند که وضعیت نمونه ای از یک رکورد را نمایش دهد.
این ستونها به شرح زیر هستند:
- Start Date: زمانی که وضعیت آن نمونه از رکورد فعال میشود.
- End Date: زمانی که وضعیت آن نمونه از رکورد غیر فعال میشود.
- Record Status: وضعیتهای یک رکورد را نشان میدهد، که حداقل به شکل Active یا Inactive است.
- # Version: این ستون که اختیاری میباشد، ورژن آن نمونه از رکورد را ثبت میکند.
برای مثال شکل زیر؛ بیانگر وضعیت اولیه رکوردی در این الگو است:
فرض کنید که این رکورد در تاریخ March 2 , 2010 در سیستم مبداء تغییر میکند. فرآیند ETL این تغییر را شناسائی میکند و همانند تصویر زیر؛ به شکل نمونه ای ثانویه از این رکورد، اقدام به درج آن میکند.
توجه داشته باشید زمانی که رکورد دوم در جدول درج میشود، به منظور بازتاب این تغییر؛ رکورد اول به شکل زیر بروزرسانی میگردد:
- End Date: تا این زمان وضعیت این رکورد فعال بوده است.
- Record Status:که Active به Inactive تغییر پیدا میکند.
در برخی از پیاده سازیهای DW عمدتاً از الگوی Versioned Insert استفاده میشود و هرگز از الگوی Update استفاده نمیشود. مزیت این استراتژی در این است که تمامی تاریخچه تغییرات ردیابی و ثبت میشود. به هر روی غالباً هزینه ثبت کردن این تغییرات منجر به ایجاد نسخههای زیادی از تغییرات میشود. تیم DW برای مواردی که تغییرات متاثر از گزارشات تاریخچه ای نیستند، میتوانند الگوی Update را در نظر گیرند.
3-3- Update Pattern
الگوی Update روی رکورد موجود، تغییرات سیستم مبداء را بروزرسانی میکند. مزیت این روش در این است که همواره یک رکورد وجود دارد و در نتیجه باعث ایجاد Queryهای کارآمدتر میشود. تصویر زیر بیانگر ستون هایی است که برای پشتیبانی از الگوی Update بایست ایجاد کرد.
این ستونها به شرح زیر هستند:
- Record Status: وضعیتهای یک رکورد را نشان میدهد که حداقل به شکل Active یا Inactive است.
- # Version: این ستون که اختیاری میباشد، ورژن آن نمونه از رکورد را ثبت میکند.
موارد اصلی الگوی Update عبارتند از:
- تاریخ ثبت نمیشود. ابزاری ارزشمند برای نظارت بر داده ها، تغییرات تاریخی است و زمانی که ممیزی داده رخ میدهد؛ میتواند مفید واقع شود.
- بروزرسانیها یک الگوی مبتنی بر مجموعه هستند. استفاده از بروزرسانی هر بار یک رکورد در ابزار ETL خیلی کارآمد (موجه) نیست.
یک روش دیگر برای در نظر گرفتن موارد فوق؛ اضافه کردن یک جدول برای درج ورژنها به الگوی Update است که در شکل زیر نشان داده شده است.
اضافه نمودن یک جدول تاریخچه، که تمامی تغییرات سیستم مبداء را ثبت میکند؛ نظارت و ممیزی دادهها را نیز فراهم میکند و همچنین بروزرسانیهای کارآمد مبتنی بر مجموعه را برای جداول DW به ارمغان میآورد.
3-4- Versioned Insert: Net Changes
این الگو غالباً در جداول حجیم Fact که بروزرسانی آنها پر هزینه است استفاده میشود. شکل زیر منطق استفاده شده در این الگو را نشان میدهد.
توجه داشته باشید در این الگو:
- مقادیر مالی و عددی محاسبه شده؛ به عنوان یک Net Change از نمونه قبلی رکورد در جدول Fact ذخیره میشود.
- هیچ گونه فعالیت Post Processing صورت نمیگیرد (از قبیل بروزرسانی جداول Fact پس از کامل شدن Data Flow). هدف استفاده از این الگو اجتناب از بروزرسانی روی جداول بسیار حجیم میباشد.
- عدم بروزرسانی و همچنین اندازه جدول Fact زمینه ای را فراهم میکند که منطق شناسائی رکوردهای تغییریافته پیچیده تر میشود. این پیچیدگی از آنجا ناشی میشود که نیاز به مقایسه رکوردهای جدول Fact آتی با جدول Fact موجود میباشد.
4- Data Integration Best Practices
هم اکنون پس از آشنایی با مفاهیم و الگوهای توزیع دادهها به ارائه تعدادی نمونه میپردازیم؛ که بتوان این ایدهها و الگوها را در عمل پوشش داد.
4-1- Basic Data Flow Patterns
هر یک از الگوهای Update Pattern و Versioned Insert Pattern میتوانند برای انواعی از جداول بکار روند که معروفترین آنها توسط Kimball ساخته شده اند.
- (Slowly Changing Dimension Type I (SCD I: از Update Pattern استفاده میکند.
- (Slowly Changing Dimension Type II (SCD II: از Versioned Insert Pattern استفاده میکند.
- Fact Table: نوع الگویی که استفاده میکند به نوع جدول Fact ای که Load خواهد شد بستگی دارد.
4-1-1- Update Pattern
مطابق تصویر زیر جدولی که تنها حاوی ورژن فعلی رکورد هاست؛ از Update Dataflow Pattern استفاده میکند.
مواردی که در مورد این گردش کاری باید در نظر داشت به شرح زیر است:
- این Data Flow فقط سطرهایی را به یک مقصد اضافه خواهد کرد. SSIS دارای گزینه “Table or view fast load” میباشد که بارگذاریهای انبوه و سریع را پشتیبانی میکند.
- درون یک Data Flow بروزرسانی رکوردها را میتوان با استفاده از تبدیل OLE DB Command انجام داد. توجه داشته باشید خروجیهای این تبدیل در یک دستور Update به ازای هر رکورد بکار میرود؛ مفهوم بروزرسانی انبوه در این Data Flow وجود ندارد. بدین ترتیب الگوی فعلی ارائه شده؛ تنها رکوردها را درج میکند و هرگز در این Data Flow رکوردها Update نمیشوند.
- هر جدول دارای یک جدول تاریخچه است که برای ذخیره همه فعالیتهای مرتبط با آن بکار میرود. یک رکورد در جدول تاریخچه زمانی درج خواهد شد؛ که رکورد مبداء در مقصد وجود داشته باشد ولی دارای مقداری متفاوت باشد.
- راه دیگر فرستادن تغییرات رکوردها به یک جدول کاری است که پس از پایان یافتن فرآیند Update ، خالی (Truncate) میشود.
- مزیت نگهداری تمامی رکوردها در یک جدول تاریخچه؛ ایجاد یک دنباله ممیزی است که میتواند برای نظارت بر دادهها به منظور نمایان ساختن موارد مطرح شده توسط مصرف کنندههای کسب و کار استفاده شود.
- گزینههای متفاوتی برای تشخیص تغییرات رکوردها وجود دارد که در ادامه به شرح آنها میپردازیم.
شکل زیر نمایش دهنده چگونگی پیاده سازی Update Dataflow Pattern در یک SSIS میباشد:
این SSIS شامل عناصر زیر است:
- Destination table lookup:
به منظور تشخیص اینکه رکورد در جدول مقصد وجود دارد از “lkpPersonContact” استفاده میکنیم.
- Change detection logic:
با استفاده از “DidRecordChange” مبداء و مقصد مقایسه میشوند. اگر تفاوتی بین مبداء و مقصد وجود نداشت؛ رکورد نادیده گرفته میشود. چنانچه بین مبداء و مقصد تفاوت وجود داشت؛ رکورد در جدول تاریخچه درج خواهد شد.
- Detection Inserts:
رکوردها در جدول مقصد درج خواهند شد در صورتیکه در آن وجود نداشته باشند.
- Destination History Inserts:
رکوردها در جدول تاریخچه مقصد درج خواهند شد، در صورتیکه (در مقصد) وجود داشته باشند.
پس از اتمام Data Flow یک روال Post-processing مسئولیت بروزرسانی رکوردهای جدول اصلی و رکوردهای ذخیره شده در جدول تاریخچه را بر عهده دارد که میتواند مطابق تصویر زیر با استفاده از یک Execute Process Task پیاده سازی شود.
PostProcess مسئولیت اجرای تمامی فعالیتهای زیر را در این الگو برعهده دارد که شامل:
- بروزرسانی رکوردهای جداول با استفاده از رکوردهای درج شده در جدول تاریخچه.
- درج تمامی رکوردهای جدید (نسخه اولیه و در درون جدول تاریخچه). کلید اصلی جداولی که ستون آنها IDENTITY است مقدار نامشخصی دارد؛ تا زمانی که درج صورت گیرد، این به معنای آن است که پیش از انتقال آنها به جدول تاریخچه نیاز است منتظر درج شدن آنها باشیم.
4-1-2- Update Pattern – ETL Framework
تصویر زیر بیانگر انجام این عملیات با استفاده از ابزارهای ETL است.
در نگاه نخستین ممکن است Data Flow از نوع اصلی خود پیچیدهتر به نظر آید؛ که در واقع این گونه نیز هست، زیرا در فاز توسعه بیشتر Frameworkها جهت پیاده سازی به یک زمان اضافهتری نیاز دارند. به هر روی این زمان جهت اجتناب از هزینه روزانه تطبیق دادهها گرفته خواهد شد.
مزایای حاصل شده از افزودن این منطق اضافی عبارت است از:
- پشتیبانی از ستون هایی که کارهای ممیزی و نظارت بر دادهها را آسانتر میکنند.
- تعداد سطرها شاخص مناسبی است که میتواند بهبود آن Data Flow خاص را فراهم کند. ناظر اطلاعات با استفاده از تعداد رکوردها میتواند ناهنجاریها را شناسائی کند.
بهره برداران ETL و ناظران اطلاعات میتوانند با استفاده از خلاصه تعداد رکوردها درک بیشتری درباره فعالیتهای آن کسب کنند. پس از آنکه تعداد رکوردها، مشکوک به نظر آمد؛ تحقیقات بیشتری میتواند اتفاق افتد. (با عمیقتر شدن در جزئیات گزارشات)
4-1-3- Versioned Insert Pattern
جدولی که به صورت Versioned Insert پر شده است میتواند از Versioned Insert Dataflow Pattern استفاده کند. همانند شکل زیر که گردش کار در آن برای کارآئی بیشتر بازنگری شده است.
توجه داشته باشید Data Flow در این روش شامل:
- تمامی رکوردهای جدید و تغییر یافته در جدول Versioned Insert قرار میگیرند.
- این روش دارای Data Flow سادهتری نسبت به الگوی Update میباشد.
شکل زیر SSIS versioned insert data flow pattern را نشان میدهد:
تعدادی نکته در Data Flow فوق وجود دارد که عبارتند از:
- در شیء “lkpDimGeography” گزینه “Redirect rows to no match output” با مقدار “Ignore Failures” تنظیم شده است.
- شیء “DidRecordChange” بررسی میکند چنانچه ستونهای مبداء و مقصد یکسان باشند، آیا کلید اصلی جدول مقصد Not Null است. اگر این عبارت True ارزیابی شود، رکورد نادیده گرفته میشود.
- منطق شناسائی تغییرات دربردارنده تغییرات ستون داده ای در مبداء نمیباشد.
- ستون و تعداد رکوردها مشابه با Data Flow قبلی (ETL Framework) میباشد.
4-1-4- Update vs. Versioned Insert
الگوی Versioned Insert نسبت الگوی Update دارای پیاده سازی سادهتر و فعالیتهای I/O کمتری است. از منظر دیگر، جدولی که از الگوی Update استفاده میکند، دارای تعداد رکوردهای کمتری است که میتواند به معنای Performance بهتر نیز تعبیر شود. ممکن است سوالی مطرح شود، اینکه چرا برای انجام کار به جدول تاریخچه نیاز است؛ این جدول را که نمیتوان Truncate نمود، پس چرا به منظور بروزرسانی از جدول اصلی استفاده میشود؟ پاسخ این پرسش در این است که جدول تاریخچه، ناظر اطلاعات و ممیزین داده را قادر میسازد، تغییرات در طول زمان را پیگیری نمایند.
4-2- Dimension Patterns
بروزرسانی Dimension موارد زیر را شامل میشود:
- پیگیری تاریخچه
- انجام بروزرسانی
- تشخیص رکوردهای جدید
- مدیریت surrogate keys
چنانچه با یک Dimension کوچک مواجه هستید (با مقدار هزاران رکورد یا کمتر، که با صدها هزار رکورد یا بیشتر ضدیت دارد)، میتوانید از تبدیل “Slowly Changing Dimension” که بصورت Built-in در SSIS موجود است، استفاده نمائید. به هر روی با آنکه این تبدیل چندین ویژگی محدودکننده Performance دارد، اغلب کارآمدتر از پروسسه هایی که توسط خودتان ایجاد میشود. در واقع فرآیند بارگذاری در جداول Dimension با مقایسه دادهها بین مبداء و مقصد انجام میشود. به طور معمول مقایسه روی یک ورژن جدید و یا مجموعه ای از سطرهای جدید یک جدول با مجموعه دادههای موجود در جدول متناظرش صورت میگیرد. پس از تشخیص چگونگی تغییر در داده ها، یک سری عملیات درج و بروزرسانی انجام میشود. شکل زیر نمونه ای از پردازش سریع در Dimension را نمایش میدهد؛ که شامل مراحل اساسی زیر است:
- منبع فوقانی سمت چپ، رکوردها را در یک SSIS از یک سیستم مبداء (یا یک سیستم میانی) به شکل Pull دریافت میکند. منبع فوقانی سمت راست، دادهها را از خود جدول Dimension به شکل Pull دریافت میکند.
- با استفاده از Merge Join رکوردها از طریق Source Key شان مقایسه میشوند. (در شکل بعدی جزئیات این مقایسه نمایش داده شده است.)
- با استفاده از یک Conditional Spilt دادهها ارزیابی میشوند؛ سطرها یا مستقیماً در جدول Dimension درج میشوند (منبع تحتانی سمت چپ) و یا در یک جدول عملیاتی (منبع تحتانی سمت راست) جهت انجام بروزرسانی درج میشوند.
- در گام پایانی (که نمایش داده نشده) مجموعه ای از بروزرسانی بین جدول عملیاتی و جدول Dimension صورت میگیرد.
با Merge Join ارتباطی بین رکوردهای مبداء و رکوردهای مقصد برقرار میشود. (در این مثال “CustomerAlternateKey”). هنگامی که از این دیدگاه استفاده میکنید، خاطر جمع شوید که نوع Join با مقدار “Left outer join” تنظیم شده است؛ بدین ترتیب قادر هستید تا رکوردهای جدید را از مبداء تشخیص دهید؛ از آنجا که هنوز در جدول Dimension قرار نگرفته اند.
گام پایانی به منظور تشخیص اینکه آیا رکورد، جدید یا تغییر یافته است (یا بلاتکلیف است)، مقایسه داده هاست. شکل زیر نمایش میدهد چگونه این ارزیابی با استفاده از تبدیل “Conditional Spilt” صورت میگیرد.
Conditional Spilt مستقیماً با استفاده از یک Adapter تعریف شده روی مقصد یا یک جدول کاری بروزرسانی که از یک Adapter تعریف شده روی مقصد استفاده میکند؛ توسط مجموعه دستور Update زیر، رکوردها را در جدول Dimension قرار میدهد. دستور Update زیر مستقیماً با استفاده از روش Join روی جدول Dimension و جدول کاری، مجموعه ای را بصورت انبوه بروزرسانی میکند.
UPDATE AdventureWorksDW2008R2.dbo.DimCustomer SET AddressLine1 = stgDimCustomerUpdates.AddressLine1 , AddressLine2 = stgDimCustomerUpdates.AddressLine2 , BirthDate = stgDimCustomerUpdates.BirthDate , CommuteDistance = stgDimCustomerUpdates.CommuteDistance , DateFirstPurchase = stgDimCustomerUpdates.DateFirstPurchase , EmailAddress = stgDimCustomerUpdates.EmailAddress , EnglishEducation = stgDimCustomerUpdates.EnglishEducation , EnglishOccupation = stgDimCustomerUpdates.EnglishOccupation , FirstName = stgDimCustomerUpdates.FirstName , Gender = stgDimCustomerUpdates.Gender , GeographyKey = stgDimCustomerUpdates.GeographyKey , HouseOwnerFlag = stgDimCustomerUpdates.HouseOwnerFlag , LastName = stgDimCustomerUpdates.LastName , MaritalStatus = stgDimCustomerUpdates.MaritalStatus , MiddleName = stgDimCustomerUpdates.MiddleName , NumberCarsOwned = stgDimCustomerUpdates.NumberCarsOwned , NumberChildrenAtHome = stgDimCustomerUpdates.NumberChildrenAtHome , Phone = stgDimCustomerUpdates.Phone , Suffix = stgDimCustomerUpdates.Suffix , Title = stgDimCustomerUpdates.Title , TotalChildren = stgDimCustomerUpdates.TotalChildren FROM AdventureWorksDW2008.dbo.DimCustomer DimCustomer INNER JOIN dbo.stgDimCustomerUpdates ON DimCustomer.CustomerAlternateKey = stgDimCustomerUpdates.CustomerAlternateKey
4-3- Fact Table Patterns
جداول Fact به پردازشهای منحصر به فردی نیازمند هستند، نخست به کلیدهای Surrogate جدول Dimension نیاز دارند تا Measureهای محاسبه شدنی را بدست آورند. این اعمال از طریق تبدیلات Lookup، Merge Join و Derived Column صورت میگیرد. با بروزرسانی ها، تفاضل رکوردها و یا Snapshot بیشتر این فرآیندهای دشوار انجام میشوند.
4-3-1- Inserts
روی اغلب جداول Fact عمل درج صورت میگیرد؛ که کار متداولی در جدول Fact میباشد. شاید سادهترین کار که در فرآیند ساخت ETL صورت میگیرد، عملیات درج روی تنها تعدادی از جدول Fact میباشد. درج کردن در صورت لزوم بارگذاری انبوه داده ها، مدیریت شاخصها و مدیریت پارتیشنها را شامل میشود.
4-3-2- Updates
بروزرسانی روی جداول Fact معمولاً به یکی از سه طریق زیر انجام میگیرد:
- از طریق یک تغییر یا بروزرسانی رکورد
- از طریق یک دستور Insert خنثی کننده (Via an Insert of a compensating transaction)
- با استفاده از یک SQL MERGE
در موردی که تغییرات با فرکانس کمی روی جدول Fact صورت میگیرد و یا فرآیند بروزرسانی قابل مدیریت است؛ سادهترین روش انجام یک دستور Update روی جدول Fact میباشد. نکته مهمی که هنگام انجام بروزرسانی باید به خاطر داشته باشید، استفاده از روش بروزرسانی مبتنی بر مجموعه است؛ به همان طریق که در قسمت الگوهای Dimension ذکر آن رفت.
در طریقی دیگر (درج compensating) میتوان اقدام به درج رکورد تغییر یافته نمود، تا ترجیحاً بروزرسانی روی آن صورت گیرد. این استراتژی به سادگی دادههای جدول Fact میان سیستم مبداء و مقصد را که تغییر یافته اند، به صورت یک رکورد جدید درج خواهد کرد. تصویر زیر مثالی از اجرای موارد فوق را نمایش میدهد.
در آخرین روش از یک دستور SQL MERGE استفاده میشود که در آن با استفاده از ادغام و مقایسه، تمامی دادههای جدید و تغییر یافته جدول Fact، درج و یا بروزرسانی میشوند. نمونه ای از استفاده دستور Merge به شرح زیر است:
MERGE dbo.FactSalesQuota AS T USING SSIS_PDS.dbo.stgFactSalesQuota AS S ON T.EmployeeKey = S.EmployeeKey AND T.DateKey = S.DateKey WHEN MATCHED AND BY target THEN INSERT(EmployeeKey, DateKey, CalendarYear, CalendarQuarter, SalesAmountQuota) VALUES(S.EmployeeKey, S.DateKey, S.CalendarYear, S.CalendarQuarter, S.SalesAmountQuota) WHEN MATCHED AND T.SalesAmountQuota != S.SalesAmountQuota THEN UPDATE SET T.SalesAmountQuota = S.SalesAmountQuota ;
4-3-3- Managing Inferred Members
زمانیکه یک ارجاع در جدول Fact به یک عضو Dimension که هنوز بارگذاری نشدهاست بوجود آید؛ یک Inferred Member تعبیر میشود. به سه طریق میتوان این Inferred Memberها را مدیریت نمود:
- رکوردهای جدول Fact پیش از درج اسکن شوند؛ ایجاد هر Inferred Member در Dimension و سپس بارگذاری رکوردها در جدول Fact
- در طول عملیات بارگذاری روی Fact؛ هر رکورد مفقوده شده به یک جدول موقتی ارسال شود، رکوردهای مفقوده شده به Dimension اضافه شود، در ادامه مجدداً آن رکوردهای Fact در جدول Fact بارگذاری شوند.
- در یک Data Flow زمانی که یک رکورد مفقود شده، بلاتکلیف تعبیر میشود؛ آن زمان یک رکورد به Dimension اضافه شود و Surrogate Key بدست آمده را برگردانیم؛ سپس Dimension بارگذاری شود.
شکل زیر این موارد را نمایش میدهد:
Static Reflection
قابلیت Dynamic reflection یا به اختصار همان reflection متداول، از اولین نگارشهای دات نت فریم در دسترس است و امکان دسترسی به اطلاعات مرتبط با کلاسها، متدها، خواص و غیره را در زمان اجرا مهیا میسازد. تابحال به کمک این قابلیت، امکان تهیهی ابزارهای پیشرفتهی زیر مهیا شده است:
انواع و اقسام
- فریم ورکهای آزمون واحد
- code generators
- ORMs
- ابزارهای آنالیز کد
و ...
برای مثال فرض کنید که میخواهید برای یک کلاس به صورت خودکار، متدهای آزمون واحد تهیه کنید (تهیه یک code generator ساده). اولین نیاز این برنامه، دسترسی به امضای متدها به همراه نام آرگومانها و نوع آنها است. برای حل این مساله باید برای مثال یک parser زبان سی شارپ یا اگر بخواهید کاملتر کار کنید، به ازای تمام زبانهای قابل استفاده در دات نت فریم ورک باید parser تهیه کنید که ... کار سادهای نیست. اما با وجود reflection به سادگی میتوان به این نوع اطلاعات دسترسی پیدا کرد و نکتهی مهم آن هم این است که مستقل است از نوع زبان مورد استفاده. به همین جهت است که این نوع ابزارها را در فریم ورکهایی که فاقد امکانات reflection هستند، کمتر میتوان یافت. برای مثال کیفیت کتابخانههای آزمون واحد CPP در مقایسه با آنچه که در دات نت مهیا هستند، اصلا قابل مقایسه نیستند. برای نمونه به یکی از معظمترین فریم ورکهای آزمون واحد CPP که توسط گوگل تهیه شده مراجعه کنید : (+)
قابلیت Reflection ، مطلب جدیدی نیست و برای مثال زبان جاوا هم سالها است که از آن پشتیبانی میکند. اما نگارش سوم دات نت فریم ورک با معرفی lambda expressions ، LINQ و Expressions در یک سطح بالاتر از این Dynamic reflection متداول قرار گرفت.
تعریف Static Reflection :
استفاده از امکانات Reflection API بدون بکارگیری رشتهها، به کمک قابلیت اجرای به تعویق افتادهی LINQ، جهت دسترسی به متادیتای المانهای کد، مانند خواص، متدها و غیره.
برای مثال کد زیر را در نظر بگیرید:
//dynamic reflection
PropertyInfo property = typeof (MyClass).GetProperty("Name");
MethodInfo method = typeof (MyClass).GetMethod("SomeMethod");
چقدر خوب میشد اگر این قابلیت بجای dynamic بودن (مشخص شدن در زمان اجرا)، استاتیک میبود و در زمان کامپایل قابل بررسی میشد. این امکان به کمک lambda expressions و expression trees دات نت سه بعد، میسر شده است. کلیدهای اصلی Static Reflection کلاسهای Func و Expression هستند. با استفاده از کلاس Func میتوان lambda expression ایی را تعریف کرد که مقداری را بر میگرداند و توسط کلاس Expression میتوان به محتوای یک delegate دسترسی یافت. ترکیب این دو، قدرت دستیابی به اطلاعاتی مانند PropertyInfo را در زمان طراحی کلاسها، میدهد؛ با توجه به اینکه:
- کاملا توسط intellisense موجود در VS.NET پشتیبانی میشود.
- با استفاده از ابزارهای refactoring قابل کنترل است.
- از همه مهمتر، دیگری خبری از رشتهها نبوده و همه چیز تحت کنترل کامپایلر قرار میگیرد.
و شاید هیچ قابلیتی به اندازهی Static Reflection در این چندسال اخیر بر روی اکوسیستم دات نت فریم ورک تاثیرگذار نبوده باشد. این روزها کمتر کتابخانه یا فریم ورکی را میتوانید پیدا کنید که از Static Reflection استفاده نکند. سرآغاز استفاده گسترده از آن به Fluent NHibernate بر میگردد؛ سپس در انواع و اقسام mocking frameworks ، ORMs و غیره استفاده شد و مدتی است که در ASP.NET MVC نیز مورد استفاده قرار میگیرد (برای مثال TextBoxFor معروف آن):
public string TextBoxFor<T>(Expression<Func<T,object>> expression);
<%= this.TextBoxFor(model => model.FirstName); %>
یک مثال ساده از تعریف و بکارگیری Static Reflection :
public PropertyInfo GetProperty<T>(Expression<Func<T, object>> expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
throw new InvalidOperationException("Not a member access.");
return memberExpression.Member as PropertyInfo;
}
برای نمونه Fluent NHibernate در پشت صحنه متد Map ، به کمک متدی شبیه به GetProperty فوق، a => a.Address1 را به رشته متناظر خاصیت Address1 تبدیل کرده و جهت تعریف نگاشتها مورد استفاده قرار میدهد:
public class AddressMap : DomainMap<Address>
{
public AddressMap()
{
Map(a => a.Address1);
}
}
جهت اطلاع؛ قابلیت استفاده از «کد به عنوان اطلاعات» هم مفهوم جدیدی نیست و برای مثال زبان Lisp چند دهه است که آنرا ارائه داده است!
برای مطالعه بیشتر:
از آنجائیکه مدتی قسمتی از کارم مرتبط بود به طراحی ایمیلهای خودکار برای برنامههای تهیه شده (مثلا، ایمیلهای مرحله به مرحله یک گردش کاری ... اطلاع رسانیهای خودکار از وضعیت دادهها، گزارشاتی از برنامهها که به صورت خبرنامههای ایمیلی در بازههای زمانی مشخصی به اشخاص مشخص شده ارسال میشد و غیره)، لازم میدونم خلاصهای از تجربیات برخورد با کاربران را در این مورد در ادامه ذکر کنم، شاید مفید باشد.
1) حتما در انتهای ایمیل خودکار ارسالی، ساعت و تاریخ شمسی ارسال پیام را نیز ذکر کنید.
عموما از آنجائیکه سیستم استاندارد ارسال ایمیل بر اساس تاریخ میلادی است و تقریبا تمام کلاینتهای دریافت ایمیل موجود نیز توانایی شمسی سازی تاریخ دریافت و ارسال ایمیل را ندارند (مگر با یک سری افزونه و یا دستکاری در سیستم عامل که آنچنان خوشایند و مرسوم نیست)، ذکر تاریخ شمسی در انتهای پیام بسیار مفید خواهد بود و در اکثر اوقات استناد به ایمیلهای دریافت شده بر اساس تاریخ دریافت آنها است.
2) سعی کنید از بکارگیری عناوین (subject) ثابت جهت ارسال ایمیلهای خودکار پرهیز کنید.
دقیقا یادم میاد زمانیکه برای مدیر عامل شرکتی سه بار پشت سرهم ایمیلی با یک عنوان ارسال شده بود بنده را بازخواست کردند که چرا برنامهی شما ایمیل تکراری ارسال میکند!
بله، سعی میکنند محتوا را از روی عنوان ایمیل حدس بزنند و زمانیکه یک عنوان ثابت را برای ایمیلهای خودکار خود انتخاب کردید، تکراری به نظر خواهند رسید یا حتی ممکن است به اشتباه پیش از خوانده شدن حذف شوند.
برای مثال فرض کنید ایمیل ارجاع کاری را قرار است به صورت خودکار ارسال کنید. انتخاب عنوان ثابت برای مثال "ارجاع کار جدید" اشتباه است! این عنوان باید بر اساس نوع کار هر بار به صورت پویا متغیر باشد؛ مثلا: "ارجاع کار جدید: از طرف : ... ، موضوع: ... ، درجه اهمیت: ..." که این سه نقطهها باید توسط برنامه هر بار پر شوند.
3) هر چه میتوانید اطلاعات بیشتری را توسط یک ایمیل خودکار منتقل کنید.
مورد قبل را در نظر بگیرید. ذکر "ارجاع کار جدید ..." در عنوان و سپس مجددا ذکر همین عنوان به عنوان بدنهی ایمیل خودکار به زودی ایمیلهای شما را تبدیل به نوعی Spam آزار دهنده خواهد کرد. کار جدیدی ارجاع شده است؟ آیا میتوان خلاصهای از این کار را به همراه ایمیل نیز ارسال کرد تا کاربر حتما برای مشاهدهی ریز جزئیات کار به برنامه مراجعه نکند و این ایمیل واقعا ارزش مطالعه را داشته باشد و سبب تسریع در انجام کارها شود؟
برای مثال ذکر کلی این مورد که درخواست مرخصی جدیدی را باید تائید یا رد کنید، کافی نیست. ریز جزئیات مرخصی را هم به همراه ایمیل ارسال کنید.
4) ایمیل شما باید حاوی لینکی جهت باز کردن برنامهی تحت وب مرتبط نیز باشد.
کاری ارجاع شده است؟ بهتر است لینک پویایی را جهت هدایت کاربر به صفحهی مرتبط رسیدگی به همان کار ارجاعی ارسال کنید. به این صورت زحمت او را کمتر کرده و یک مرحله گزارش گیری را حذف خواهید کرد. یا حداقل یک محل مراجعهی کلی بعدی را به این صورت میتوان ارائه داد.
5) از بکارگیری قسمت from ایی مانند DoNotReply@Site.Com خودداری کنید.
کاربر دریافت کنندهی ایمیل باید بداند که در صورت وجود مشکل باید به کجا مراجعه کند؟ چه کسی این ایمیل را ارسال کرده؟
هرچند برنامه به صورت خودکار تمام قسمتهای این ایمیل ارسالی را تهیه میکند اما اگر خبرنامهی تنظیم شدهای نیست، حتما شخص ارسال کنندهای دارد. یا حداقل یک ایمیل عمومی را برای این مورد تنظیم کنید (ایمیلی که وجود خارجی داشته و هر از چندگاهی بررسی میشود).
6) رنگ زمینه و اندازهی قلم مناسبی را انتخاب کنید.
دقیقا برای هر کدام از موارد ذکر شده چندین بار مشکل داشتهام! عموما کسانی که ایمیلها را دریافت میکنند سن و سال دار هستند. بنابراین انتخاب فونت tahoma با اندازهی 8 یا pt 7 سبب توبیخ زود هنگام شما خواهد شد!
همچنین هر چه سادهتر بهتر. دقیقا مشکلات از زمانی آغاز میشوند که طرحی را انتخاب کنید یا رنگی را برای زمینه بکار ببرید. اینجا است که هر روز یک سلیقهی تحمیلی را باید پذیرا باشید.
7) دقیقا مشخص کنید که ایمیل دریافتی آیا رونوشت است یا خیر!
همان مبحث ارجاع کار را در نظر بگیرید. پس از اینکه سیستم راه اندازی شد، مدیر یکی از قسمتها چند روز بعد این درخواست را "حتما" ارسال خواهد کرد: رونوشت تمام کارهای ارجاعی به کلیه پرسنل بخش و همچنین ریز اقدامات آنها باید برای بنده نیز ارسال شود.
در اینجا تنها افزودن قسمت CC به ایمیلهای خودکار کفایت نمیکند. حتما به صورت درشت در بالای ایمیل، قبل از شروع بدنه ذکر کنید که ایمیل دریافتی یک رونوشت است. در غیر اینصورت باید پاسخگوی علت دریافت ایمیلهایی باشید که به درخواست خودشان CC شده است!
8) از ایمیلهای خودکار برنامه log تهیه کنید.
بارها به این مساله برخورد کردهام که اشخاص برای شانه خالی کردن از انجام کار محوله، سعی در تخریب کار شما خواهند داشت. خیلی ساده عنوان میکنند که ایمیلی را دریافت نکردهاند. حالا شما بیاید ثابت کنید که اگر سیستم مشکل داشت کلا برای هیچ کسی ایمیل ارسال نمیشد، نه فقط برای شما. در اینگونه مواقع وجود یک لاگ از ایمیلها (ثبت در بانک اطلاعاتی) و ارجاع به آنها بسیار راه گشا است.
9) راهی را برای خلاص شدن از شر دریافت ایمیلهای خودکار نیز پیش بینی کنید!
همان مورد 7 را در نظر بگیرید. دو روز اول خیلی ذوق خواهند کرد! روز سوم وقتی انبوهی از ایمیلها را دریافت کردند، مشکل شما هم شروع خواهد شد. بنابراین امکان تنظیم دریافت یا عدم دریافت ایمیل را حتما در برنامه قرار دهید. یا حداقل نحوهی ایجاد یک پوشه جدید و فیلتر کردن ایمیلهای رسیده و هدایت خودکار آنها به این پوشهی جدید را آموزش دهید.
خوب! حالا به نظر شما این ایمیل خودکار ارسالی سایت IDevCenter که اخیرا اضافه شده است چه نمرهای را کسب میکند؟
- تاریخ شمسی در انتهای ایمیل ندارد.
- عنوانها ثابت هستند.
- هیچ جزئیاتی ارائه نشده است.
- لینک مرتبط دارد.
- قسمت from مناسبی دارد.
- ساده است؛ خوب است! فقط اندازه قلم آن بهتر است یک شماره بزرگتر شود.
- بحث رونوشت اینجا مورد ندارد.
- بحث لاگ ... شخصی است.
- امکان تنظیم دریافت ایمیل پیش بینی شده است.
نمره از 7 : 3.5
معرفی کتابخانهی DNTPersianUtils.Core
سریعترین روش خواندن آیتمهای یک لیست
فرض کنید یک لیست از اعداد به تعداد مثلا 1000000 عضو دارید و میخواهید با خواندن آنها عملیاتی را انجام دهید. در این ویدیوی کوتاه نشان داده میشود که از بین روشهای معمول خواندن آیتمهای این لیست ، سریعترین روش کدام است. مدت زمان روش پیشنهادی در این ویدیو، نصف زمان معمولی است که عموما با استفاده از حلقه for یا foreach استفاده میکنیم (و جالبتر اینکه این دو روش یعنی: for و foreach در اکثر موارد، سریعترین روش خواندن محتویات یک کالکشن هستند). روش پیشنهادی یکی از دو روش زیر است :
List<int> items = new List<int>(new int[1000]); foreach(int item in CollectionMarshal.AsSpan(items)) { .......... } var asSpan = CollectionMarshal.AsSpan(items); for(int i; i < asSpan.Length; i++) { var item = asSpan[i]; }
با توجه به پیشرفت تکنولوژی و معماریهای مدرن، در اپلیکیشنهای امروزی سرعت و کارایی از مولفه هایی هستند که بیشتر احساس نیاز میشوند و درصورت عدم دارا بودن این مولفه ها، اپلیکیشن مورد توجه کاربران قرار نخواهد گرفت و درنهایت سودی به بار نخواهد آمد.
یکی از روشهای کسب این مولفهها استفاده از کشینگ در سطح ایپلیکیشن خودتون است.
در زیر پکیجی رو براتون معرفی میکنم که میتوانید در پروژههای دات نت خودتون کشینگ رو براحتی پیاده سازی کنید.
شما با استفاده از پکیج PolyCache براحتی و در سریعترین زمان میتونید کشینگ رو پیاده سازی کنید.
روال و جزئیات پیاده سازی در گیت هاب پروژه قرار گرفته است.
«... در اولین قسمت از این مجموعه در مورد تعریف کلی و مزایا و معایب میکروسرویسها صحبت کردیم. در دومین قسمت به سراغ API Gatewayها، نقش آنها در توسعه میکروسرویس و ویژگیها یک API Gateway صحبت کردیم. سپس در قسمت سوم در مورد ارتباط بین سرویسها و انواع روشهای برقراری ارتباط صحبت کردیم و نهایتا در چهارمین قسمت در مورد تکنولوژیهای توسعه میکروسرویسها صحبت کردیم. سپس در پنجمین قسمت از این مجموعه در مورد Service Discovery مطالبی را بررسی کردیم. در نهایت در ششمین قسمت از این مجموعه در مورد دادهها و مدیریت گردش اطلاعات در میکروسرویسها مطالبی را بیان کردیم. حالا در این قسمت میخواهیم در مورد نحوه انتشار و نصب میکروسرویسها مطالبی را بررسی کنیم ...»
استراتژیهای استقرار نرم افزار
استقرار نرم افزار شامل تمام فعالیتهایی است که یک نرم افزار را برای استفادهی کاربران نهایی آماده میکند. از آنجایی که هر سیستم نرم افزاری شرایط و ویژگیهای منحصر به فردی دارد، فرایندها و روشهای استقرار آن باید به طور خاص و دقیق برای همان سیستم تعریف شود. بنابراین استقرار نرم افزار، یک فرایند کلی است که باید با توجه به شرایط و خصوصیات هر سیستم نرم افزاری به صورت دقیق تعریف شود.... در این مطلب به ابتدا به تعریف استراتژی (Strategy)، استقرار نرم افزار (Software Deployment) اشاره شده و در ادامه دو تا از استراتژیهای پایه استقرار نرم افزار معرفی و بررسی شده است.