نظرات مطالب
Protocol Buffers فرمتی برای تبادل دیتا
چند تا مساله را میگم و دیگه این بحث رو ادامه نمیدم به هیچ وجه (چون مساله‌ی مطرح شده هیچ ربطی به این مقاله‌ی خاص نداره و فقط چون این وبسایت بازدید کننده‌ی زیادی داره مجبورم کامل توضیح بدم که ما اشتباها خط غلط به کسی ندیم و گمراه نکنیم کسی رو ناخواسته)
1) موضوع این مقاله فقط و فقط protocol buffers بود و هیچ ربطی به هیچ زبان برنامه نویسی نداشت و وارد شدن به این مساله در اینجا اشتباه است، من حتی در این مقاله توضیح ندادم که با استفاده از grpc و protobuf مستقیما نمیتونیم از طریق browser درخواست بدیم، و وارد کردن این مساله و همینطور لایبری‌های مختلف جاوا اسکریپتی و سی شارپی جایگاهش در این نقاله نبود چون نگاه خیلی بالاتر از این بود تا اینکه به سطح بیاییم و بخواهیم راجع به یک کتابخونه خاص و یک زبان خاص و مقایسه بین آنها رو انجام بدیم!
2) از اینجا میتونید مشاهد کنید که حداقل به صورت رسمی (اینکه توی گوگل سرچ زده بشه و اولین نتیجه به عنوان سند در پاسخ داده بشه مورد قبول نیست، چون قطعا ممکنه به صورت غیر رسمی ابزار هایی باشه که مورد قبول در تولید یک سیستم روی پروداکشن نیست!) زبان‌های swift, rust , ... هنوز به صورت رسمی ساپورت نمیشند! پس اگر نگاهمون به عنوان توسعه دهنده فراتر از یک مثال hello-world باشه باید اینو مد نظر بگیریم.
3) تکنولوژی باید به ساختار و سیستم ما کمک کنه نه اینکه خودش bottleneck سیستم بشه، تکنولوژی جذابه ولی نکته‌ی مهم اینه که ما اول از همه به این مساله دقت کنیم که باید به عنوان برنامه نویس روی تکنولوژی سوار باشیم نه برعکس (بزرگترین مشکل وقتی پیش میاد که هیجان زده ابزارو تکنولوژی جدید آشنا بشیم و بخواهیم در سیستم استفاده کنیم! هیجان بیش از حد قاتل سسیستمه) با انتخاب اشتباهمون کل سازمان رو دچار مشکل نکنیم، مخصوصل وقتی با یک تکنولوژی آشنا میشیم وفور لایبری‌های مختلف نباید ما رو گمراه کنه!
4) وقتی دیدتون به عنوان توسعه دهنده از مونولوتیک خارج میشه و به مایکروسرویس نگاه میکنید حتما باید از زاویه‌ی خیلی بالاتری نگاه کنید، جایی که توقع میره همه‌ی سیستم بتونه به همدیگه به بهترین نحو بدون مشکل کار کنه پس انتخاب ابزار خیلی اهمیتش بالاتره از تکنولوژی جدید که شاید تازه هم باهاش آشنا شده باشید، api-gateway  به عنوان یک اصل برای معماری ماکروسرویس پذیرفته شده و حتی خیلی توصیه میشه
5) نگاه به سیستم‌های طراحی شده‌ی دنیا هم خیلی بهمون کمک میکنه حتی پروژه‌های بزرگی مثله etcd از gateway استفاده کرده اند چرا که سنگ بزرگ نشانه نزدنه و ریسک تغییرات خیلی بزرگ و بنیادین کل نرم افزار رو به چالش میشکه و علاوه بر اینکه هزینه‌ی زیاد learning curve برای کله تیم داره، ریسک شکست رو هم خیلی بالا میبره، شما فرض کنید که برنامه نویس فرانت اند که تا حالا تو عمرش به غیر از restful api چیز دیگه ای ندیده ناگهان با کلی ابزار جدید آشنا باید بشه که حتی ممکنه رسمی و صد در صد قابل اتکا هم نباشه، این خودش شروع fail شدن یک سیستم رو نتیجه میده!
در مقاله‌ی بعدی سعی میکنم مثال عملی با استفاده از زبان‌های برنامه نویسی مختلف بزنم و توضیحات جزئی‌تری بدم و موارد بیشتر رو فقط و فقط به خواننده که خودش قطعا توسعه دهنده هست و میتونه بالا و پایین رو ببینه میسپارم!
ممنون
نظرات مطالب
Extension Methods
لطفا این بحث رو ادامه ندید. در یک سری از دسترسی‌های نویسنده‌ها به زودی تجدید نظر خواهد شد. چه کسانی که ابراز تمایل کرده بودند و فقط وقت من رو جهت ثبت آن‌ها نصف روزی تلف کردند؛ چه کسانی که مطالب سطحی ارسال می‌کنند.
با تشکر

ضمن اینکه یک مطلب را هم مد نظر داشته باشید. اینجا هدف بیشتر ذکر یک سری نکته است به همین جهت اسم سایت tips دارد (نکات ریز).
اسم سایت encyclopedia نیست که برای هر مطلبی قرار باشد کتاب نوشته شود. اگر نوشته شد، چقدر خوب؛ اگر نه ... یک نکته ریز جدید یاد گرفتید. این هم خوب.
همچنین هدف از این سایت خواننده عام با سطح مطالعه و اطلاعات صفر نیست و نبوده. از روز اولش اینطور نبوده و نخواهد بود.

یک مطالب رو هم فراموش نکنید. به قول مولوی: «گر تو بهتر میزنی بستان بزن»
در طی یک ماهی که این سایت در حالت تعلیق بود ... من هر چقدر سایت‌های فارسی زبان فنی برنامه نویسی رو گشتم که دوتا مطلب به درد بخور پیدا کنم ... چیزی نیافتم. هیچی! واقعا دریغ از یک مطلب فنی به درد بخور که منتشر شده باشد. حالا همین عده منتظرند سریع بریزند سر یک نفر شروع کنند به داد و قال.
باز تکرار می‌کنم: «گر تو بهتر میزنی بستان بزن»


اشتراک‌ها
کتابخانه ای برای ++C جهت ثبت لاگ

Single header only C++ logging library. It is extremely light-weight, robust, fast performing, thread and type safe and consists of many built-in features. It provides ability to write logs in your own customized format. It also provide support for logging your classes, third-party libraries, STL and third-party containers etc. 

کتابخانه ای برای ++C جهت ثبت لاگ
مطالب
بهبود کارآیی LINQ در دات نت 7
LINQ یا همان Language-Integrated Query، یک زبان ساده‌ی کوئری نوشتن یکپارچه‌ی با دات نت است. به کمک آن می‌توان اعمال پیچیده‌ای را بر روی اشیاء، به زبانی ساده بیان کرد و امروزه تقریبا توسط تمام توسعه دهندگان دات نت مورد استفاده قرار می‌گیرد. اما ... این سادگی، بهایی را نیز به همراه دارد: کمتر بودن سرعت اجرا و همچنین افزایش مصرف حافظه. با توجه به گستردگی استفاده‌ی از LINQ، اگر بهبودی در این زمینه حاصل شود، بر روی کارآیی تمام برنامه‌های دات نتی تاثیر خواهد گذاشت و این امر در دات نت 7 محقق شده‌است. کارآیی متدهای LINQ to Objects در دات نت 7 (مانند متدهای Enumerable.Max, Enumerable.Min, Enumerable.Average, Enumerable.Sum) به شدت افزایش یافته و این افزایش گاهی حتی بیشتر از 10 برابر نسبت به نگارش‌های قبلی دات نت است؛ اما چگونه به چنین کارآیی رسیده‌اند؟


تدارک یک آزمایش برای بررسی میزان افزایش کارآیی متدهای LINQ در دات نت 7

در ادامه یک آزمایش ساده‌ی بررسی کارآیی متدهای Enumerable.Max, Enumerable.Min, Enumerable.Average, Enumerable.Sum را با استفاده از کتابخانه‌ی معروف BenchmarkDotNet مشاهده می‌کنید:
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Collections.Generic;
using System.Linq;


[MemoryDiagnoser(displayGenColumns: false)]
public partial class Program
{
  static void Main(string[] args) =>
    BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);

  [Params (10, 10000)]
  public int Size { get; set; }
  private IEnumerable<int> items;

  [GlobalSetup]
  public void Setup()
  {
    items = Enumerable.Range(1, Size).ToArray();
  }  

  [Benchmark]
  public int Min() => items.Min();

  [Benchmark]
  public int Max() => items.Max();

  [Benchmark]
  public double Average() => items.Average();

  [Benchmark]
  public int Sum() => items.Sum();
}
برای آزمایش آن، یکبار target framework پروژه را بر روی net6.0 و بار دیگر بر روی net7.0 قرار داده و برنامه را اجرا می‌کنیم. خلاصه‌ی مفهومی نتایج حاصل به صورت زیر است که ... شگفت‌انگیز هستند!
در مورد کار با آرایه‌ها:


- زمان اجرای یافتن Min در آرایه‌های کوچک، در دات نت 7، نسبت به دات نت 6، حدودا 10 برابر کاهش یافته و اگر این آرایه بزرگتر شود و برای مثال حاوی 10 هزار المان باشد، این زمان 20 برابر کاهش یافته‌است.
- این کاهش زمان‌ها برای سایر متدهای LINQ نیز تقریبا به همین صورت است؛ منها متد Sum که اندازه‌ی آرایه، تاثیری را بر روی نتیجه‌ی نهایی ندارد.
- همچنین در دات نت 7، با فراخوانی متدهای LINQ، افزایش حافظه‌ای مشاهده نمی‌شود.

در مورد کار با لیست‌ها:


- در دات نت 6، اعمال صورت گرفته‌ی توسط LINQ بر روی آرایه‌ها، نسبت به لیست‌ها، همواره سریعتر است.
- در دات نت 7 هم در مورد مجموعه‌های کوچک، وضعیت همانند دات نت 6 است. اما اگر مجموعه‌ها بزرگتر شوند، تفاوتی بین مجموعه‌ها و آرایه‌ها وجود ندارد و حتی وضعیت مجموعه‌ها بهتر است: کارآیی کار با لیست‌ها 32 برابر بیشتر شده‌است!


اما چگونه در دات نت 7، چنین بهبود کارآیی خیره‌کننده‌ای در متدهای LINQ حاصل شده‌است؟

برای بررسی چگونگی بهبود کارآیی متدهای LINQ در دات نت 7 باید به نحوه‌ی پیاده سازی آن‌ها در نگارش‌های مختلف دات نت مراجعه کرد. برای مثال پیاده سازی متد الحاقی Min تا دات نت 6 به صورت زیر است:
public static int Min(this IEnumerable<int> source)
{
  if (source == null)
  {
    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
  }

  int value;
  using (IEnumerator<int> e = source.GetEnumerator())
  {
    if (!e.MoveNext())
    {
      ThrowHelper.ThrowNoElementsException();
    }

    value = e.Current;
    while (e.MoveNext())
    {
      int x = e.Current;
      if (x < value)
      {
        value = x;
      }
    }
  }
  return value;
}
این متد نسبتا ساده‌است. یک IEnumerable را دریافت کرده و سپس با استفاده از متد MoveNext، مقدار فعلی را با مقدار بعدی مقایسه می‌کند. در این مقایسه، کوچکترین مقدار ذخیره می‌شود تا در نهایت به انتهای مجموعه برسیم.
اما ... پیاده سازی این متد در دات نت 7 متفاوت است:
public static int Min(this IEnumerable<int> source) => MinInteger(source);

private static T MinInteger<T>(this IEnumerable<T> source)
  where T : struct, IBinaryInteger<T>
{
  T value;

  if (source.TryGetSpan(out ReadOnlySpan<T> span))
  {
    if (Vector.IsHardwareAccelerated && 
        span.Length >= Vector<T>.Count * 2)
    {
      .... // Optimized implementation
      return ....;
    }
  }
  .... //Implementation as in .NET 6
}
در اینجا در ابتدا سعی می‌شود تا یک ReadOnlySpan از مجموعه‌ی ارائه شده، تهیه شود. اگر این کار میسر نشد، کدهای همان روش قبلی دات نت 6 که توضیح داده شد، اجرا می‌شود. البته در آزمایشی که ما تدارک دیدیم، چون از لیست‌ها و آرایه‌ها استفاده شده بود، همواره امکان تهیه‌ی یک ReadOnlySpan از آن‌ها میسر است. بنابراین به قسمت اجرایی همانند دات نت 6 نمی‌رسیم.
اما ... ReadOnlySpan چیست؟ نوع‌های Span و ReadOnlySpan، یک ناحیه‌ی پیوسته‌ی مدیریت شده و مدیریت نشده‌ی حافظه را بیان می‌کنند. یک Span از نوع ref struct است؛ یعنی تنها می‌تواند بر روی stack قرار گیرد که مزیت آن، عدم نیاز به تخصیص حافظه‌ی اضافی و بهبود کارآیی است. همچنین ساختار داخلی Span در سی شارپ 11 اندکی تغییر کرده‌است که در آن از ref fields جهت دسترسی امن به این ناحیه‌ی از حافظه استفاده می‌شود. پیشتر از نوع داخلی ByReference برای اشاره به ابتدای این ناحیه‌ی از حافظه استفاده می‌شد که به همراه بررسی امنیتی در این باره نبود.

پس از دریافت ReadOnlySpan، به سطر زیر می‌رسیم:
if (Vector.IsHardwareAccelerated && span.Length >= Vector<T>.Count * 2)
که بررسی می‌کند آیا سخت افرار فعلی از قابلیت‌های SIMD برخوردار است یا خیر؟ اگر بله، اینبار با استفاده از ریاضیات برداری شتاب یافته‌ی توسط سخت افزار، محاسبات را انجام می‌دهد:
private static T MinInteger<T>(this IEnumerable<T> source)
where T : struct, IBinaryInteger<T>
{
  .... 
  if (Vector.IsHardwareAccelerated && span.Length >= Vector<T>.Count * 2)
  {
    var mins = new Vector<T>(span);
    index = Vector<T>.Count;
    do
    {
      mins = Vector.Min(mins, new Vector<T>(span.Slice(index)));
      index += Vector<T>.Count;
    }
    while (index + Vector<T>.Count <= span.Length);

    value = mins[0];
    for (int i = 1; i < Vector<T>.Count; i++)
    {  
      if (mins[i] < value)
      {
        value = mins[i];
      }
    }
  ....
}
بنابراین به صورت خلاصه در دات نت 7 با استفاده از بکارگیری نوع‌های ویژه‌ی Span و نوع‌های برداری شتاب‌یافته‌ی توسط اکثر سخت افزارهای امروزی، سبب بهبود قابل ملاحظه‌ی کارآیی متدهای LINQ شده‌اند.
نظرات مطالب
خلاصه اشتراک‌های روز شنبه 14 آبان 1390
- مشکل این قالب جدید استاندارد خود بلاگر این است که css های آن از آدرس‌های بلاگر که فیلتر شده آدرس دهی می‌شود و احتمالا سایت را مشکل دار نمایش خواهد داد.- خودکار است. من از گوگل ریدر یا هر آدرس دیگری می‌فرستم به گوگل+. از آنجا به بعد یک برنامه براش نوشتم که این‌ها رو می‌خونه و سپس سر ساعت ارسال می‌کنه به بلاگر.
مطالب
Owin چیست ؟ قسمت اول
مطمئنا اکثر شما برنامه نویسان با معماری IIS و ASP.NET کمابیش آشنایی دارید
Request از سمت کلاینت به IIS ارسال می‌شود، و عموما بسته به نوع درخواست کلاینت یا به یک Static File مپ می‌شود( مثلا به یک عکس )، و یا به یک ISAPI
ISAPI کدی است که عموما با ++C نوشته می‌شود، و برای درخواست آمده از سمت کلاینت کاری را انجام می‌دهد
یکی از این ISAPI‌ها برای ASP.NET است، که درخواست کلاینت را به یک کد مبتنی بر NET. مپ می‌کند ( به همین علت به آن ASP.NET می‌گویند )
نکته ای که در خطوط فوق به وضوح دیده می‌شود، وابستگی شدید ASP.NET به IIS است
بدیهتا کدی که بر روی بستر ASP.NET نوشته می‌شود نیز وابستگی فوق العاده ای به IIS دارد، که یکی از بدترین نوع این وابستگی‌ها در ASP.NET Web Forms دیده می‌شود.
خب، این مسئله چه مشکلاتی را ایجاد می‌کند ؟
مشکل اول که شاید کمتر به چشم بیاید، بحث کندی اجرای بار اول برنامه‌‌های ASP.NET است.
اما مشکل دوم عدم توانایی در نوشتن کد برنامه، بدون وابستگی به وب سرور (در اینجا IIS ) است، که این مشکل دوم روز به روز در حال جدی‌تر شدن است.
این مشکل دوم را برنامه نویسان جاوا سالهاست که با آن درگیرند، نکته این است که بین دو وب سرور در نحوه پردازش یک درخواست کلاینت تفاوت هایی وجود دارد، که بالطبع این تفاوت‌ها در نحوه‌ی اجرای کد بالاخره خودش را جاهایی نشان می‌دهد، این که بگوییم رفتار وب سرورها نباید متفاوت باشد کمی مسخره است، زیرا تفاوت آنها  با یکدیگر باعث شده که سرعت یکسان و امکانات یکسانی نداشته باشند و هر کدام برای یک سناریوی خاص مناسب‌تر باشند
این مسئله برای ما نیز روز به روز دارای اهمیت بیشتری می‌شود، دیگر این که Web Server ما فقط IIS صرف باشد، سناریوی متداول در پروژه‌های Enterprise نیست
در چه جاهایی می‌توان یک برنامه را هاست کرد ؟
IIS به همراه ASP.NET
IIS بدون ASP.NET ( می‌خواهیم برنامه بر روی IIS هاست شود، ولی کاری با ASP.NET نداریم )
CLR AppDomains
و وب سرورهای لینوکسی در صورت اجرای برنامه بر روی Mono
و ...
هم اکنون به میزان زیادی مشکل شفاف شده است، مطابق با معماری فعلی داریم
 Request >> IIS >>  aspnet_isapi.dll >>  System.Web.dll >> Your codes
مشکل دیگری که وجود دارد این است که اگر تیمی بخواهد فریم ورکی برای برنامه نویسان نهایی فراهم کند، باید آنرا بر روی اکثر گزینه‌های هاست موجود سازگار  کنید، برای   مثال مشاهده می‌کنید که ASP.NET Signal R را هم می‌توان بر روی IIS و ASP.NET هاست کرد و هم بر روی یک App Domain کاملا معمولی  و علاوه بر این که تیم   SignalR باید این هزینه مضاعف را پرداخت کند، خروجی برای ما نیز چندان خوشایند نیست، برای مثال اجرای همزمان ASP.NET SignalR و ASP.NET  Web API اگر چه که بر  روی هاستی به غیر از ASP.NET نیز امکان پذیر است، اما متاسفانه به عنوان دو بازیگر جدا از هم کار می‌کنند و عملا تعاملی با یکدیگر ندارند، مگر این  که بر روی ASP.NET  هاست شوند، و یا بسیاری از امکانات Routing موجود در WCF بر روی بستری غیر از ASP.NET کار نمی‌کند.
بدیهی است که این بازار پر آشوب به نفع هیچ کس نیست.
و اما راه حل چیست ؟
تعدادی از برنامه نویسان حرفه ای NET. دور یکدیگر جمع شدند و طی بررسی هایشان به این نتیجه رسیدند که هاست‌های مختلف نقاط اشتراک بسیار زیادی دارند و  تفاوت‌ها نباید باعث این میزان مشکل شود.
پس استانداری را طراحی کردند با نام OWIN  یا Open Web Interface for .NET
این استاندارد به صورت کاملا ریز به طراحی هر چیزی را که شما به آن فکر کنید پرداخته است، Request, Cookie, Response, Web Sokcet و ...
اما همانطور که از نامش مشخص است این یک استاندارد است و پیاده سازی ندارد، و هر هاستی باید یک بار این استاندارد را بر روی خود پیاده سازی کند
خبر خوش این است که تا این لحظه اکثر هاست‌های مهم این استاندارد را پیاده سازی کرده اند و یا در دست پیاده سازی دارند
پروژه Helios برای IIS
پروژه Katana  برای IIS به در کنار و سازگار با ASP.NET برای پروژه هایی که تا این لحظه از امکانات سطح پایین ASP.NET استفاده زیادی کرده اند و فرصت تغییر ساختاری ندارند
پروژه هایی برای App Domains و ...
مرحله‌ی بعدی این است که فریم ورک‌ها خوشان را با Owin سازگار کنند
معروف‌ترین فریم ورک هایی که تا این لحظه اقدام به انجام این کار کرده اند، عبارتند از:
ASP.NET Web API
ASP.NET MVC
ASP.NET Identity
ASP.NET Signal R ( در حال حاضر Signal R فقط بر روی Owin قابلیت استفاده دارد )
بدیهی است که زمانی که پروژه ASP.NET Web API بر روی استاندارد OWIN نوشته می‌شود، دیگر نیازی به تحمل هزینه مضاعف برای سازگاری خود با انواع هاست ها ندارد و این مسئله توسط Katana، Helios  و ... انجام شده است، که بالطبع بزرگترین نفع آن برای ما جلوگیری از چند باره کاری توسط تیم Web API و ... است که بالطبع در  زمان کمتر امکانات بیشتری را به ما ارائه می‌دهند.
البته واضح است فریم ورک هایی که با کلاینت و درخواست‌ها کاری ندارند، با این مقولات کاری ندارند، پس Entity Framework و ... از این داستان مستثنا هستند.
و علاوه بر این فریم ورک هایی با طراحی اشتباه و قدیمی مانند ASP.NET Web Forms به صورت کلی قابلیت سازگار شدن با این استاندارد را ندارند، زیرا کاملا به ASP.NET  وابسته هستند
و در نهایت در مرحله‌ی بعدی لازم است شما نیز از فریم ورک هایی استفاده کنید که مبتنی بر OWIN هستند، یعنی برای مثال پروژه بعدی تان را مبتنی بر ASP.NET MVC و ASP.NET Web API و ASP.NET Identity پیاده سازی کنید، در این صورت شما می‌توانید برنامه ای بنویسید که به Web Server هیچ گونه وابستگی ندارد.
به این صورت کد زدن چند مزیت بزرگ دیگر هم دارد که از کم اهمیت‌ترین آنها شروع می‌کنیم:
1- سرعت بسیار بالاتر برنامه در هاست‌های غیر ASP.NET ای، مانند زمانی که شما از IIS به صورت مستقیم و بدون وابستگی به System.Web.dll استفاده می‌کنید.
توجه کنید که حتی در این حالت هم می‌توانید از ASP.NET Web API و Signal R و Identity استفاده کنید و تا 25% سرعت بیشتری داشته باشید ( بسته به سناریو ) 
2- قابلیت توسعه آسانتر و با قابلیت نگهداری بالاتر پروژه‌های Enterprise، برای مثال در یکی از پروژه‌ها من مجبور بودم از ASP.NET Web API به صورتی استفاده کنم  که هم توسط کلاینت JavaScript ای استفاده شود، و هم توسط کدهای Controller‌های MVC ( بدون استفاده مستقیم از کد سرویس با رفرنس زدن به سرویس‌ها البته )  که خوشبختانه OWIN به خوبی از پس این کار بر آمد، و عملا یک سرویس Web API را هم بر روی IIS هاست کردم و هم داخل یک AppDomain
3- در چند سال آینده که اکثریت مطلق سایت‌ها از این روش استفاده کنند ( شما چه بدانید و چه ندانید اگر در برنامه خودتان از Signal R نسخه 2 دارید استفاده می‌کنید،حتما از OWIN استفاده کرده اید )، مایکروسافت می‌تواند دست به تغییرات اساسی‌تری بزند، برای مثال معماری جدیدی از IIS ارائه دهد که مشکلات ساختاری فراوان  فعلی  IIS  را   که  از حوصله توضیح این مقاله خارج است را نداشته باشد، و فقط یک پیاده سازی OWIN جدید بر روی آن ارائه دهد و برنامه‌های ما بدون تغییر بر روی آن نیز کار  کنند، و یا  این که بتواند تعدادی از فریم ورک‌های با طراحی قدیمی را راحت‌تر از دور خارج کند، مانند Web Forms
نکته پایانی، اگر هم اکنون پروژه ای دارید که در داخل آن از ASP.NET استفاده شده، و برای مثال تعدای فرم ASP.NET Web Forms نیز دارد، نگران نباشید، کماکان می‌توانید از Owin برای سایر قسمت‌ها مانند Web API استفاده کنید، البته در این حالت تاثیری در بهبود سرعت اجرای برنامه مشاهده نخواهید کرد، اما برای مهاجرت و اعمال تغییرات این آسانترین روش ممکن است
در قسمت بعدی، مثالی را شروع می‌کنیم مبتنی بر ASP.NET Web API، ASP.NET Identity و Helios
اشتراک‌ها
پیشنهاد اضافه شدن Nullable reference types به C# 7
در حال حاضر reference types در زبان #C نال پذیر هستند. جهت بالا بردن میزان امنیت زبان، پیشنهاد شده‌است که حالت پیش فرض reference types به غیرنال پذیر تغییر یابد و اگر علاقمند بودید که نال پذیر شوند، همانند nullable value types فعلی مانند int? x، نوع ?T را تعریف کنید. البته این مورد یک پیشنهاد از طرف اعضای تیم سی‌شارپ است و عده‌ای با آن موافق هستند (جهت بالا بردن ضریب امنیت و کاهش null reference exceptions) و عده‌ای خیر (گیج کننده‌است و کدهای فعلی را با مشکل مواجه می‌کند؛ یا خطاهای زیادی را توسط کامپایلر گزارش خواهد کرد).
پیشنهاد اضافه شدن Nullable reference types به C# 7
مطالب
C# 12.0 - Interceptors
به C# 12 و دات‌نت 8، ویژگی «آزمایشی» جدیدی به نام Interceptors اضافه شده‌است که به آن «monkey patching» هم می‌گویند. هدف از آن، جایگزین کردن یک پیاده سازی، با پیاده سازی دیگری است. به این ترتیب توسعه دهندگان دات‌نتی می‌توانند فراخوانی متدهایی خاص را ره‌گیری کرده (interception) و سپس آن‌را به فراخوانی یک پیاده سازی جدید، هدایت کنند.


Interceptor چیست؟

از زمان ارائه‌ی NET 8 preview 6 SDK. به بعد، امکان ره‌گیری هر متدی از کدهای برنامه، به دات‌نت اضافه شده‌است؛ به همین جهت از واژه‌ی Interceptor/ره‌گیر در اینجا استفاده می‌شود. خود تیم دات‌نت از این قابلیت در جهت بازنویسی پویای قسمت‌هایی از کدهای زیرساخت دات‌نت که از Reflection استفاده می‌کنند، با نگارش‌های کامپایل شده‌ی مختص به برنامه‌ی شما، کمک می‌گیرند. به این ترتیب سرعت و کارآیی برنامه‌های دات‌نت 8، بهبود قابل ملاحظه‌ای را پیدا کرده‌اند. برای مثال ahead-of-time compilation (AOT) در دات‌نت 8 و ASP.NET Core 8x بر اساس این ویژگی پیاده سازی شده‌است. این ویژگی جدید، مکمل source generators است که در نگارش‌های پیشین دات‌نت ارائه شده بود.


بررسی  Interceptors با تهیه‌ی یک مثال ساده

فرض کنید می‌خواهیم فراخوانی متد GetText زیر را ره‌گیری کرده و سپس آن‌را با نمونه‌ی دیگری جایگزین کنیم:
namespace CS8Tests;

public class InterceptorsSample
{
    public string GetText(string text)
    {
        return $"{text}, World!";
    }
}
برای اینکار ابتدا نیاز است یک فایل جدید را به نام InterceptsLocationAttribute.cs با محتوای زیر به پروژه اضافه کرد:
namespace System.Runtime.CompilerServices;

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public sealed class InterceptsLocationAttribute : Attribute
{
    public InterceptsLocationAttribute(string filePath, int line, int character)
    {
    }
}
همانطور که در مقدمه‌ی بحث هم عنوان شد، این ویژگی هنوز آزمایشی است و نهایی نشده و ویژگی فوق نیز هنوز به دات‌نت اضافه نشده‌است. به همین جهت فعلا باید آن‌را به صورت دستی به پروژه اضافه کرد و احتمالا در نگارش‌های بعدی دات‌نت، امضای آن تغییر خواهد کرد ... یا حتی ممکن است بطور کامل حذف شود!

سپس فرض کنید فراخوانی متد GetText در فایل Program.cs برنامه به صورت زیر انجام شده‌است:
using CS8Tests;

var example = new InterceptorsSample();
var text = example.GetText("Hello");
Console.WriteLine(text); //Hello, World!
یعنی متد GetText، در سطر چهارم و کاراکتر 20 ام آن فراخوانی شده‌است. این اعداد مهم هستند!

در ادامه از این اطلاعات در ره‌گیر سفارشی زیر استفاده خواهیم کرد:
using System.Runtime.CompilerServices;

namespace CS8Tests;

public static class MyInterceptor
{
    [InterceptsLocation("C:\\Path\\To\\CS8Tests\\Program.cs", 4, 20)] 
    public static string InterceptorMethod(this InterceptorsSample example, string text)
    {
        return $"{text}, DNT!";
    }
}
این ره‌گیر که به صورت متدی الحاقی برای کلاس InterceptorsSample دربرگیرنده‌ی متد GetText تهیه می‌شود، کار جایگزینی فراخوانی آن‌را در سطر چهارم و کاراکتر 20 ام فایل Program.cs انجام می‌دهد. امضای پارامترهای این متد، باید با امضای پارامترهای متد ره‌گیری شده، یکی باشد.

اکنون اگر برنامه را اجرا کنیم ... با خطای زیر مواجه می‌شویم:
 error CS9137: The 'interceptors' experimental feature is not enabled in this namespace. Add
'<InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);CS8Tests</InterceptorsPreviewNamespaces>'
to your project.
عنوان می‌کند که این ویژگی آزمایشی است و باید فایل csproj. را به صورت زیر تغییر داد تا بتوان از آن استفاده نمود:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <!--<NoWarn>Test001</NoWarn>-->
    <InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);CS8Tests</InterceptorsPreviewNamespaces>
  </PropertyGroup>
</Project>
اینبار برنامه کامپایل شده و اجرا می‌شود. در این حالت خروجی جدید برنامه، خروجی تامین شده‌ی توسط ره‌گیر سفارشی ما است:
Hello, DNT!


سؤال: آیا ره‌گیری انجام شده، در زمان کامپایل انجام می‌شود یا در زمان اجرا؟

برای این مورد می‌توان به Low-Level C# code تولیدی مراجعه کرد. برای مشاهده‌ی یک چنین کدهایی می‌توانید از منوی Tools->IL Viewer برنامه‌ی Rider استفاده کرده و در برگه‌ی ظاهر شده، گزینه‌ی Low-Level C# آن‌را انتخاب نمائید:
using CS8Tests;
using System;
using System.Runtime.CompilerServices;

[CompilerGenerated]
internal class Program
{
  private static void <Main>$(string[] args)
  {
    Console.WriteLine(new InterceptorsSample().InterceptorMethod("Hello"));
  }

  public Program()
  {
    base..ctor();
  }
}
همانطور که مشاهده می‌کنید، این ره‌گیری و جایگزینی، در زمان کامپایل انجام شده و کامپایلر، به‌طور کامل نحوه‌ی فراخوانی متد GetText اصلی را به متد ره‌گیر ما تغییر داده و بازنویسی کرده‌است.


سؤال: آیا این قابلیت واقعا کاربردی است؟!

اکنون شاید این سؤال مطرح شود که ... واقعا چه کسی قرار است مسیر کامل یک فایل، شماره سطر و شماره ستون فراخوانی متدی را به اینگونه در اختیار سیستم ره‌گیری قرار دهد؟! آیا واقعا این قابلیت، یک قابلیت کاربردی و مناسب است؟!
اینجا است که اهمیت source generators مشخص می‌شود. توسط source generators دسترسی کاملی به syntax trees وجود دارد و همچنین یکسری اطلاعات تکمیلی مانند FilePath و سپس CSharpSyntaxNodeها که دسترسی به داده‌های متد ()GetLocation را دارند که مکان دقیق سطر و ستون‌های فراخوانی‌ها را مشخص می‌کند.


کاربردهای فعلی ره‌گیرها در دات نت 8

در دات نت 8، این موارد با استفاده از ره‌گیرها بهینه سازی شده و سرعت آن‌ها افزایش یافته‌اند:
- فراخوانی‌هایی که تمام اطلاعات آن‌ها در زمان کامپایل فراهم است، مانند Regex.IsMatch(@"a+b+") که از یک الگوی ثابت و مشخص استفاده می‌کند، ره‌گیری شده و پیاده سازی آن با کدی استاتیک، جایگزین می‌شود.
- در ASP.NET Minimal API، استفاده از lambda expressions جهت ارائه‌ی تعاریفی مانند:
app.MapGet("/products", handler: (int? page, int? pageLength, MyDb db) => { ... })
مرسوم است. این نوع فراخوانی‌ها نیز توسط ره‌گیرها برای جایگزینی handler آن‌ها با کدهای استاتیک، جهت بالابردن کارآیی و کاهش تخصیص‌های حافظه انجام می‌شود.
- بهبود کارآیی foreach loops جهت استفاده از ریاضیات برداری و SIMD در صورت امکان.
- بهبود کارآیی تزریق وابستگی‌ها، زمانیکه به تعاریف مشخصی مانند ()<provider.Register<MyService ختم می‌شود.
- بجای استفاده از expression trees در زمان اجرای برنامه، اکنون می‌توان کدهای SQL معادل را در زمان کامپایل برنامه تولید کرد.
- بهبود کارآیی Serializers، زمانیکه از یک نوع مشخص مانند ()<Serialize<MyType استفاده می‌شود و کامپایلر می‌تواند آن‌را با کدهای زمان کامپایل، جایگزین کند.


محدودیت‌های ره‌گیرها در دات‌نت 8

- ره‌گیرهای دات‌نت 8 فقط با متدها کار می‌کنند.
- مسیر ارائه شده حتما باید یک مسیر کامل و مشخص باشد. یعنی اگر این قطعه کد، به سیستم دیگری منتقل شود، کامپایل نخواهد شد و امکان ارائه‌ی مسیرهای نسبی وجود ندارد.
- امضای متدها، حتما باید یکی باشد. یعنی نمی‌توان یک ره‌گیر جنریک را تعریف کرد.
مطالب
استفاده از Lambda Expression در پروژه های مبتنی بر WCF
نکته : آشنایی با مفاهیم پایه WCF برای فهم بهتر مفاهیم توصیه می‌شود.

امروزه استفاده از WCF در پروژه‌های SOA بسیار فراگیر شده است. کمتر کسی است که در مورد قدرت تکنولوژی WCF نشنیده باشد یا از این تکنولوژی در پروژه‌های خود استفاده نکرده باشد. WCF مدل برنامه نویسی یکپارچه مایکروسافت برای ساخت نرم‌افزارهای سرویس گرا است و برای توسعه دهندگان امکانی را فراهم می‌کند که راهکارهایی امن، و مبتنی بر تراکنش را تولید نمایند که قابلیت استفاده در بین پلتفرم‌های مختلف را دارند. قبل از WCF توسعه دهندگان پروژه‌های نرم افزاری برای تولید پروژه‌های توزیع شده باید شرایط موجود برای تولید و توسعه را در نظر می‌گرفتند. برای مثال اگر استفاده کننده از سرویس در داخل سازمان و بر پایه دات نت تهیه شده بود از .net remoting استفاده می‌کردند و اگر استفاده کننده سرویس از خارج سازمان یا مثلا بر پایه تکنولوژی J2EE بود از Web Service‌ها استفاده می‌شد. با ظهور WCF این تکنولوژی با هم تجمیع شدند(بهتر بگم تبدیل به یک تکنولوژی واحد شدند) و دیگر خبری از net remoting یا web service‌ها نیست.
  WCF با تمام قدرت و امکاناتی که داراست دارای نقاط ضعفی هم می‌باشد که البته این معایب (یا محدودیت) بیشتر جهت سازگار سازی سرویس‌های نوشته شده با سیستم‌ها و پروتکل‌های مختلف است.
برای انتقال داده‌ها از طریق WCF بین سیستم‌های مختلف باید داده‌های مورد نظر حتما سریالایز شوند که مثال هایی از این دست رو در همین سایت می‌تونید مطالعه کنید:
(^ )  و (^ ) و (^ )

با توجه به این که داده‌ها سریالایز می‌شوند، در نتیجه امکان انقال داده هایی که از نوع object  هستند در WCF وجود ندارد. بلکه نوع داده باید صراحتا ذکر شود و این نوع باید قابیلت سریالایز شدن را دارا باشد.برای مثال شما نمی‌تونید متدی داشته باشید که پارامتر ورودی آن از نوع delegate باشد یا کلاسی باشد که صفت [Serializable] در بالای اون قرار نداشته باشد یا کلاسی باشد که صفت DataContract برای خود کلاس و صفت DataMember برای خاصیت‌های اون تعریف نشده باشد. حالا سوال مهم این است اگر متدی داشته باشیم که پارامتر ورودی آن حتما باید از نوع delegate باشد چه باید کرد؟

برای تشریح بهتر مسئله یک مثال می‌زنم؟

سرویسی داریم برای اطلاعات کتاب ها. قصد داریم متدی بنوسیم که پارامتر ورودی آن از نوع Lambda Expression است تا Query مورد نظر کاربر از سمت کلاینت به سمت سرور دریافت کند و خروجی مورد نظر را با توجه به Query ورودی به کلاینت برگشت دهد.( متدی متداول در اکثر پروژه ها). به صورت زیر عمل می‌کنیم.

*ابتدا یک Blank Solution ایجاد کنید.

*یک ClassLibrary به نام Model ایجاد کنید و کلاسی به نام Book در آن بسازید .(همانطور که میبینید کلاس مورد نظر سریالایز شده است):

   [DataContract]
    public class Book
    {
        [DataMember]
        public int Code { get; set; }

        [DataMember]
        public string Title { get; set; }
    }
* یک WCF Service Application ایجاد کنید
یک Contract برای ارتباط بین سرور و کلاینت می‌سازیم:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.ServiceModel;

namespace WcfLambdaExpression
{
    [ServiceContract]
    public interface IBookService
    {
        [OperationContract]
        IEnumerable<Book> GetByExpression( Expression<Func<Book, bool>> expression );
    }
}
متد GetByExpression دارای پارامتر ورودی expression است که نوع آن نیز Lambda Expression  می‌باشد. حال یک سرویس ایجاد می‌کنیم:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace WcfLambdaExpression
{    
    public class BookService : IBookService
    {        
        public BookService()
        {
            ListOfBook = new List<Book>();
        }

        public List<Book> ListOfBook 
        {
            get;
            private set;
        }

        public IEnumerable<Book> GetByExpression( Expression<Func<Book, bool>> expression )
        {
            ListOfBook.AddRange( new Book[] 
            {
                new Book(){Code = 1 , Title = "Book1"},
                new Book(){Code = 2 , Title = "Book2"},
                new Book(){Code = 3 , Title = "Book3"},
                new Book(){Code = 4 , Title = "Book4"},
                new Book(){Code = 5 , Title = "Book5"},
            } );

            return ListOfBook.AsQueryable().Where( expression );
        }       
    }
}
بعد از Build پروژه همه چیز سمت سرور آماده است. یک پروژه دیگر از نوع Console ایجاد کنید و از روش AddServiceReference سعی کنید که سرویس مورد نظر را به پروژه اضافه کنید. در هنگام Add Service Reference برای اینکه سرویس سمت سرور و کلاینت هر دو با یک مدل کار کنند باید از یک Reference assembly استفاده کنند و کافی است از قسمت Advanced گزینه Reuse types in referenced assemblies را تیک بزنید و assembly‌های مورد نظر را انتخاب کنید.( در این پروژه باید Model و System.Xml.Linq را انتخاب کنید)


به طور حتم با خطا روبرو خواهید شد. دلیل آن هم این است که امکان سریالایز کردن برای پارامتر ورودی expression میسر نیست.
خطای مربوطه به شکل زیر خواهد بود:
Type 'System.Linq.Expressions.Expression`1[System.Func`2[WcfLambdaExpression.Book,System.Boolean]]' cannot be serialized. 
Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute.  
If the type is a collection, consider marking it with the CollectionDataContractAttribute.  
See the Microsoft .NET Framework documentation for other supported types
حال چه باید کرد؟
روش‌های زیادی برای بر طرف کردن این محدودیت وجود دارد. اما در این پست روشی رو که خودم از اون استفاده می‌کنم رو براتون شرح می‌دهم.
در این روش باید از XElement استفاده شود که در فضای نام System.Linq.Xml قرار دارد. یعنی آرگومان ورودی سمت کلاینت باید به فرمت Xml سریالایز شود و سمت سرور دوباره دی سریالایز شده و تبدیل به یک Lambda Expression شود. اما سریالایز کردن Lambda Expression واقعا کاری سخت و طاقت فرساست . با توجه به این که در اکثر پروژه‌ها این متد‌ها به صورت Generic نوشته می‌شوند. برای حل این مسئله بعد از مدتی جستجو، کلاسی رو پیدا کردم که این کار رو برام انجام می‌داد. بعد از مطالعه دقیق و مشاهده روش کار کلاس، تغییرات مورد نظرم رو اعمال کردم و الان در اکثر پروژه هام دارم از این کلاس استفاده می‌کنم.
یک مثال از روش استفاده :
برای اینکه از این کلاس در هر دو پروژه (سرور و کلاینت) استفاده می‌کنیم باید یک Class Library جدید به نام Common بسازید و یک ارجاع از اون رو به هر دو پروژه سمت سرور و کلاینت بدید.
سرویس و Contract بالا رو به صورت زیر باز نویسی کنید.
[ServiceContract]
    public interface IBookService
    {
        [OperationContract]
        IEnumerable<Book> GetByExpression( XElement expression );
    }
و سرویس :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Xml.Linq;

namespace WcfLambdaExpression
{
    public class BookService : IBookService
    {
        public BookService()
        {
            ListOfBook = new List<Book>();
        }

        public List<Book> ListOfBook
        {
            get;
            private set;
        }

        public IEnumerable<Book> GetByExpression( XElement expression )
        {
            ListOfBook.AddRange( new Book[] 
            {
                new Book(){Code = 1 , Title = "Book1"},
                new Book(){Code = 2 , Title = "Book2"},
                new Book(){Code = 3 , Title = "Book3"},
                new Book(){Code = 4 , Title = "Book4"},
                new Book(){Code = 5 , Title = "Book5"},
            } );

             Common.ExpressionSerializer serializer = new Common.ExpressionSerializer();

            return ListOfBook.AsQueryable().Where( serializer.Deserialize( expression ) as Expression<Func<Book, bool>> );
        }
    }
بعد از Build پروژه از روش Add Service Reference استفاده کنید و می‌بینید که بدون هیچ گونه مشکلی سرویس مورد نظر به پروژه Console اضافه شد. برای استفاده سمت کلاینت به صورت زیر عمل کنید.

using System;
using System.Linq.Expressions;
using TestExpression.MyBookService;

namespace TestExpression
{
    class Program
    {
        static void Main( string[] args )
        {
            BookServiceClient bookService = new BookServiceClient();

            Expression<Func<Book, bool>> expression = x => x.Code > 2 && x.Code < 5;

            Common.ExpressionSerializer serializer = new Common.ExpressionSerializer();

            bookService.GetByExpression( serializer.Serialize( expression ) );
        }
    }
}
بعد از اجرای پروژه، در سمت سرور خروجی‌های زیر رو مشاهده می‌کنیم.

خروجی هم به صورت زیر خواهد بود:

دریافت سورس کامل Expression-Serialization
مطالب
مروری بر مفاهیم مقدماتی NoSQL
هدف از این مبحث، آشنایی با مفاهیم پایه‌ای اغلب بانک‌های اطلاعاتی NoSQL است که به صورت مشترکی در تمام آن‌ها بکار رفته است. برای مثال بانک‌های اطلاعاتی NoSQL چگونه مباحث یکپارچگی اطلاعات را مدیریت می‌کنند؟ نحوه ایندکس نمودن اطلاعات در آن‌ها چگونه است؟ چگونه از اطلاعات کوئری می‌گیرند؟ الگوریتم‌های محاسباتی مانند MapReduce چیستند و چگونه در اینگونه بانک‌های اطلاعاتی بکار رفته‌‌اند؟ همچنین الگوهای Sharding و Partitioning  که در اغلب بانک‌های اطلاعاتی NoSQL مشترکند، به چه نحوی پیاده سازی شده‌اند.


لیست مشترکات بانک‌های اطلاعاتی NoSQL

قبل از اینکه بخواهیم وارد ریز جزئیات بانک‌های اطلاعاتی NoSQL شویم، نیاز است لیست و سرفصلی از مفاهیم اصلی و مشترک بین اینگونه بانک‌های اطلاعاتی را تدارک ببینیم که شامل موارد ذیل می‌شود:

الف) Non-Relational یا غیر رابطه‌ای
از کلمه NoSQL عموما اینطور برداشت می‌شود که در اینجا دیگر خبری از SQL نویسی نیست که در عمل برداشت نادرستی است. شاید جالب باشد که بدانید، تعدادی از بانک‌های اطلاعاتی NoSQL از زبان SQL نیز به عنوان اینترفیسی برای نوشتن کوئری‌های مرتبط، پشتیبانی می‌کنند.
کلمه NoSQL بیشتر به Non-Relational یا غیر رابطه‌ای بودن اینگونه بانک‌های اطلاعاتی بر می‌گردد. مباحثی مانند مدل‌های داده‌ای نرمال شده، اتصالات و Join جداول، در دنیای NoSQL وجود خارجی ندارند.

ب) Non-schematized/schema free یا بدون اسکیما
مفهوم مهم و مشترک دیگری که در بین بانک‌های اطلاعاتی NoSQL وجود دارد، بدون اسکیما بودن اطلاعات آن‌ها است. به این معنا که با حرکت از رکورد یک به رکورد دو،  ممکن است با دو ساختار داده‌ای متفاوت مواجه شوید.

ج) Eventual consistency یا عاقبت یک دست شدن
عاقبت یک دست شدن، به معنای دریافت دستوری از شما و نحوه پاسخ دادن به آن (یا حتی پاسخ ندادن به آن) از طرف بانک اطلاعاتی NoSQL است. برای مثال، زمانیکه یک رکورد جدید را اضافه می‌کنید، یا اطلاعات موجودی را به روز رسانی خواهید کرد، اغلب بانک‌های اطلاعاتی NoSQL این دستور را بسیار سریع دریافت و پردازش خواهند کرد. اما تفاوت است بین دریافت پیام و پردازش واقعی آن در اینجا.
اکثر بانک‌های اطلاعاتی NoSQL، پردازش و اعمال واقعی دستورات دریافتی را با یک تاخیر انجام می‌دهند. به این ترتیب می‌توان خیلی سریع به بانک اطلاعاتی اعلام کرد که چه می‌خواهیم و بانک اطلاعاتی بلافاصله مجددا کنترل را به شما بازخواهد گرداند. اما اعمال و انتشار واقعی این دستور، مدتی زمان خواهد برد.

د) Open source یا منبع باز بودن
اغلب بانک‌های اطلاعاتی NoSQL موجود، منبع باز هستند که علاوه بر بهره بردن از مزایای اینگونه پروژه‌ها، استفاده کنندگان سورس باز دیگری را نیز ترغیب به استفاده از آن‌ها کرده‌اند.

ه) Distributed یا توزیع شده
هرچند امکان پیاده سازی توزیع شده بانک‌های اطلاعاتی رابطه‌ای نیز وجود دارد، اما نیاز به تنظیمات قابل توجهی برای حصول این امر می‌باشد. در دنیای NoSQL، توزیع شده بودن جزئی از استاندارد تهیه اینگونه بانک‌های اطلاعاتی است و بر اساس این مدل ذهنی شکل گرفته‌اند. به این معنا که اطلاعات را می‌توان بین چندین سیستم تقسیم کرد، که حتی این سیستم‌ها ممکن است فواصل جغرافیایی قابل توجهی نیز با یکدیگر داشته باشند.

و) Web scale یا مناسب برای برنامه‌های تحت وب پر کاربر
امروزه بسیاری از کمپانی‌های بزرگ اینترنتی، برای مدیریت تعداد بالایی از کاربران همزمان خود، مانند فیس‌بوک، یاهو، گوگل، Linkedin، مایکروسافت و غیره، نیاز به بانک‌های اطلاعاتی پیدا کرده‌اند که باید در مقابل این حجم عظیم درخواست‌ها و همچنین اطلاعاتی که دارند، بسیار بسیار سریع پاسخ دهند. به همین جهت بانک‌های اطلاعاتی NoSQL ابداع شده‌اند تا بتوان برای این نوع سناریوها پاسخی را ارائه داد.
و نکته مهم دیگر اینجا است که خود این کمپانی‌های بزرگ اینترنتی، بزرگترین توسعه دهنده‌های بانک‌های اطلاعاتی NoSQL نیز هستند.



نحوه مدیریت یکپارچگی اطلاعات در بانک‌های اطلاعاتی NoSQL

مدیریت یکپارچگی اطلاعات بانک‌های اطلاعاتی NoSQL به علت ذات و طراحی توزیع شده آن‌ها، با نحوه مدیریت یکپارچگی اطلاعات بانک‌های اطلاعاتی رابطه‌ای متفاوت است. اینجا است که تئوری خاصی به نام CAP مطرح می‌شود که شامل یکپارچگی یا Consistency به همراه Availability یا دسترسی پذیری (همیشه برقرار بودن) و partition tolerance یا توزیع پذیری است. در تئوری CAP مطرح می‌شود که هر بانک اطلاعاتی خاص، تنها دو مورد از سه مورد مطرح شده را می‌تواند با هم پوشش دهد.
به این ترتیب بانک‌های اطلاعاتی رابطه‌ای عموما دو مورد C و P یا یکپارچگی (Consistency) و partition tolerance یا میزان تحمل تقسیم شدن اطلاعات را ارائه می‌دهند. اما بانک‌های اطلاعاتی NoSQL از این تئوری، تنها دو مورد A و P را پوشش می‌دهند (دسترسی پذیری و توزیع پذیری مطلوب).
بنابراین مفهومی به نام ACID که در بانک‌های اطلاعاتی رابطه‌ای ضامن یکپارچگی اطلاعات آن‌ها است، در دنیای NoSQL وجود خارجی ندارد. کلمه ACID مخفف موارد ذیل است:
Atomicity، Consistency، Isolation و Durability
ACID در بانک‌های اطلاعاتی رابطه‌ای تضمین شده است. در این نوع سیستم‌ها، با ایجاد تراکنش‌ها، مباحث ایزوله سازی و یکپارچگی اطلاعات به نحو مطلوبی مدیریت می‌گردد؛ اما دنیای NoSQL، دسترسی پذیری را به یکپارچگی ترجیح داده است و به همین جهت پیشتر مطرح شد که مفهوم «Eventual consistency یا عاقبت یک دست شدن» در این نوع بانک‌های اطلاعاتی در پشت صحنه بکار گرفته می‌شود. یک مثال دنیای واقعی از عاقبت یک دست شدن اطلاعات را حتما در مباحث DNS مطالعه کرده‌اید. زمانیکه یک رکورد DNS اضافه می‌شود یا به روز خواهد شد، اعمال این دستورات در سراسر دنیا به یکباره و همزمان نیست. هرچند اعمال این اطلاعات جدید در یک نود شبکه ممکن است آنی باشد، اما پخش و توزیع آن در سراسر سرورهای DNS دنیا، مدتی زمان خواهد برد (گاهی تا یک روز یا بیشتر).
به همین جهت است که بانک‌های اطلاعاتی رابطه‌ای در حجم‌های عظیم اطلاعات و تعداد کاربران همزمان بالا، کند عمل می‌کنند. حجم اطلاعات بالا است، مدتی زمان خواهد برد تا تغییرات اعمال شوند، و چون مفهوم ACID در این نوع بانک‌های اطلاعاتی تضمین شده است، کاربران باید مدتی منتظر بمانند و نمونه‌ای از آن‌ها را با dead lockهای شایع، احتمالا پیشتر بررسی یا تجربه کرده‌اید. در مقابل، بانک‌های اطلاعاتی NoSQL بجای یکپارچگی، دسترسی پذیری را اولویت اول خود می‌دانند و نه یکپارچگی اطلاعات را. در یک بانک اطلاعاتی NoSQL، دستور ثبت اطلاعات دریافت می‌شود (این مرحله آنی است)، اما اعمال نهایی آن آنی نیست و مدتی زمان خواهد برد تا تمام اطلاعات در کلیه سرورها یک دست شوند.



نحوه مدیریت Indexing اطلاعات در بانک‌های اطلاعاتی NoSQL

اغلب بانک‌های اطلاعاتی NoSQL تنها بر اساس اطلاعات کلیدهای اصلی جداول آن‌ها index می‌شوند (البته نام خاصی به نام «جدول»، بسته به نوع بانک اطلاعاتی NoSQL ممکن است متفاوت باشد، اما منظور ظرف دربرگیرنده تعدادی رکورد است در اینجا). این ایندکس نیز از نوع clustered است. به این معنا که اطلاعات به صورت فیزیکی، بر همین مبنا ذخیره و مرتب خواهند شد.
یک مثال: بانک اطلاعاتی NoSQL خاصی به نام Hbase که بر فراز Hadoop distributed file system طراحی شده است، دقیقا به همین روش عمل می‌کند. این فایل سیستم، تنها از روش Append only برای ذخیره سازی اطلاعات استفاده می‌کند و در آن مفهوم دسترسی اتفاقی یا random access پیاده سازی نشده است. در این حالت، تمام نوشتن‌ها در بافر، لاگ می‌شوند و در بازه‌های زمانی متناوب و مشخصی سبب باز تولید فایل‌های موجود و مرتب سازی مجدد آن‌ها از ابتدا خواهند شد. دسترسی به این اطلاعات پس از تکمیل نوشتن، به علت مرتب سازی فیزیکی که صورت گرفته، بسیار سریع است. همچنین مصرف کننده سیستم نیز چون بلافاصله پس از ثبت اطلاعات در بافر سیستم، کنترل را به دست می‌گیرد، احساس کار با سیستمی را خواهد داشت که بسیار سریع است.
به علاوه Indexهای دیگری نیز وجود دارند که بر اساس کلیدهای اصلی جداول تولید نمی‌شوند و به آن‌ها ایندکس‌های ثانویه یا secondary indexes نیز گفته می‌شود و تنها تعداد محدودی از بانک‌های اطلاعاتی NoSQL از آن‌ها پشتیبانی می‌کنند. این مساله هم از اینجا ناشی می‌شود که با توجه به بدون اسکیما بودن جداول بانک‌های اطلاعاتی NoSQL، چگونه می‌توان اطلاعاتی را ایندکس کرد که ممکن است در رکورد دیگری، ساختار متناظر با آن اصلا وجود خارجی نداشته باشد.



نحوه پردازش Queries در بانک‌های اطلاعاتی NoSQL

بانک‌های اطلاعاتی NoSQL عموما از زبان کوئری خاصی پشتیبانی نمی‌کنند. در اینجا باید به اطلاعات به شکل فایل‌هایی که حاوی رکوردها هستند نگاه کرد. به این ترتیب برای پردازش و یافتن اطلاعات درون این فایل‌ها، نیاز به ایجاد برنامه‌هایی است که این فایل‌ها را گشوده و بر اساس منطق خاصی، اطلاعات مورد نظر را استخراج کنند. گاهی از اوقات زبان SQL نیز پشتیبانی می‌شود ولی آنچنان عمومیت ندارد. الگوریتمی که در این برنامه‌ها بکار گرفته می‌شود، Map Reduce نام دارد.
Map Reduce به معنای نوشتن کدی است، با دو تابع. اولین تابع اصطلاحا Map step یا مرحله نگاشت نام دارد. در این مرحله کوئری به قسمت‌های کوچکتری خرد شده و بر روی سیستم‌های توزیع شده به صورت موازی اجرا می‌شود. مرحله بعد Reduce step نام دارد که در آن، نتیجه دریافتی حاصل از کوئری‌های اجرا شده بر روی سیستم‌های مختلف، با هم یکی خواهند شد.
این روش برای نمونه در سیستم Hadoop بسیار مرسوم است. Hadoop دارای یک فایل سیستم توزیع شده است (که پیشتر در مورد آن بحث شد) به همراه یک موتور Map Reduce توکار. همچنین رده دیگری از بانک‌های اطلاعاتی NoSQL، اصطلاحا Wide column store نام دارند (مانند Hbase) که عموما به همراه Hadoop بکارگرفته می‌شوند. موتور Map Reduce متعلق به Hadoop بر روی جداول Hbase اجرا می‌شوند.
به علاوه Amazon web services دارای سرویسی است به نام Elastic map reduce یا EMR که در حقیقت مجموعه‌ی پردازش ابری است که بر مبنای Hadoop کار می‌کند. این سرویس قادر است با بانک‌های اطلاعاتی NoSQL دیگر و یا حتی بانک‌های اطلاعاتی رابطه‌ای نیز کار کند.
بنابراین MapReduce، یک بانک اطلاعاتی نیست؛ بلکه یک روش پردازش اطلاعات است که فایل‌ها را به عنوان ورودی دریافت کرده و یک فایل را به عنوان خروجی تولید می‌کند. از آنجائیکه بسیاری از بانک‌های اطلاعاتی NoSQL کار عمده‌اشان، ایجاد و تغییر فایل‌ها است، اغلب جداول اطلاعات آن‌ها ورودی و خروجی‌های معتبری برای یک موتور Map reduce به حساب می‌آیند.
در این بین، افزونه‌ای برای Hadoop به نام Hive طراحی شده است که با ارائه HiveSQL، امکان نوشتن کوئری‌هایی SQL مانند را بر فراز موتور‌های Map reduce ممکن می‌سازد. این افزونه با Hive tables خاص خودش و یا با Hbase سازگار است.



آشنایی مقدماتی با مفاهیمی مانند الگوهای Sharding و Partitioning در بانک‌های اطلاعاتی NoSQL

Sharding (شاردینگ تلفظ می‌شود) یک الگوی تقسیم اطلاعات بر روی چندین سرور است که اساس توزیع شده بودن بانک‌های اطلاعاتی NoSQL را تشکیل می‌دهد. این نوع تقسیم اطلاعات، از کوئری‌هایی به نام Fan-out پشتیبانی می‌کند. به این معنا که شما کوئری خود را به نود اصلی ارسال می‌کنید و سپس به کمک موتور‌های Map reduce، این کوئری بر روی سرورهای مختلف اجرا شده و نتیجه نهایی جمع آوری خواهد شد. به این ترتیب تقسیم اطلاعات، صرفا به معنای قرار دادن یک سری فایل بر روی سرورهای مختلف نیست، بلکه هر کدام از این سرورها به صورت مستقل نیز قابلیت پردازش اطلاعات را دارند.
امکان تکثیر و همچنین replication هر کدام از سرورها نیز وجود دارد که قابلیت بازیابی سریع و مقاومت در برابر خرابی‌ها و مشکلات را افزایش می‌دهند.
از آنجائیکه Shardها را می‌توان در سرورهای بسیار متفاوت و گسترده‌ای از لحاظ جغرافیایی قرار داد، هر Shard می‌تواند همانند مفاهیم CDN نیز عمل کند؛ به این معنا که می‌توان Shard مورد نیاز سروری خاص را در محلی نزدیک‌تر به او قرار داد. به این ترتیب سرعت عملیات افزایش یافته و همچنین بار شبکه نیز کاهش می‌یابد.