⏱️ ءPeriodicTimer در NET.؛ چه زمانی بهتر از Task.Delay است؟
خیلی وقتها در پروژههای NET. نیاز داریم یک عملیات را بهصورت دورهای اجرا کنیم:
🔹 هر ۳۰ ثانیه وضعیت سفارشها را بررسی کنیم
🔹 هر ۵ دقیقه Cache را Refresh کنیم
🔹 هر یک دقیقه Jobهای Pending را پردازش کنیم
🔹 هر چند دقیقه اطلاعات یک سرویس خارجی را Sync کنیم
اولین چیزی که خیلی از Developerها مینویسند چیزی شبیه این است:
while (!cancellationToken.IsCancellationRequested)
{
await DoSomethingAsync();
await Task.Delay(
TimeSpan.FromMinutes(5),
cancellationToken);
}
این کد کاملاً معتبر است و در بسیاری از سناریوها هم انتخاب خوبی است.
اما از NET 6. به بعد، یک API مشخص برای همین مدل سناریو داریم:
PeriodicTimerمایکروسافت آن را بهعنوان Timerای معرفی میکند که امکان waiting asynchronously for timer ticks را فراهم میکند.
⏱️ ءPeriodicTimer دقیقاً چیست؟
ء
PeriodicTimer در namespace زیر قرار دارد:System.Threading
و استفاده اصلی آن این است که بهجای Callback-based Timer، منتظر Tick بعدی Timer بمانیم:
using var timer = new PeriodicTimer(
TimeSpan.FromMinutes(5));
while (await timer.WaitForNextTickAsync(cancellationToken))
{
await DoSomethingAsync(cancellationToken);
}
اینجا یک نکته مهم وجود دارد:
ء( )
WaitForNextTickAsync یک <ValueTask<bool برمیگرداند.تا زمانی که Tick بعدی اتفاق نیفتاده، کد شما منتظر میماند و بعد از Tick وارد بدنه حلقه میشود.
اگر Timer متوقف شود، این متد میتواند
false برگرداند و حلقه تمام شود. همچنین ( )Dispose میتواند یک WaitForNextTickAsync فعال را متوقف کند و باعث شود false برگردد.🔄 پس چه فرقی با Task.Delay دارد؟
مثلاً این دو کد را ببینید.
با
Task.Delay:while (!cancellationToken.IsCancellationRequested)
{
await DoSomethingAsync(cancellationToken);
await Task.Delay(
TimeSpan.FromMinutes(5),
cancellationToken);
}
و با
PeriodicTimer:using var timer = new PeriodicTimer(
TimeSpan.FromMinutes(5));
while (await timer.WaitForNextTickAsync(
cancellationToken))
{
await DoSomethingAsync(cancellationToken);
}
از نظر نتیجه ظاهری، هر دو میتوانند یک کار را بهصورت دورهای اجرا کنند.
اما مدل ذهنی آنها متفاوت است.
ء
Task.Delay میگوید:بعد از این مدت دوباره ادامه بده.
در حالی که
PeriodicTimer میگوید:من یک Timer دورهای دارم؛ وقتی Tick بعدی رسید، اجازه بده ادامه بدهم.
برای سناریوهایی که ذاتاً periodic هستند، این مدل بسیار واضحتر است.
⚠️ اما یک اشتباه مهم
نباید تصور کنیم:
PeriodicTimer
یعنی Job شما هر دقیقاً ۵ دقیقه یکبار اجرا میشود.
فرض کنید:
Period = 5 minutes
Tick #1
↓
DoSomethingAsync()
↓
7 minutes
↓
Tick #2
در این مدت شما دوباره
WaitForNextTickAsync() را صدا نزدهاید.طبق مستندات،
PeriodicTimer برای استفاده توسط یک consumer در هر لحظه طراحی شده است و فقط یک WaitForNextTickAsync() باید همزمان در حال انتظار باشد.بنابراین نباید از آن انتظار داشته باشید که برای هر Tick یک اجرای موازی از Job ایجاد کند.
🧠 این رفتار در BackgroundService خیلی کاربردی است
مثلاً فرض کنید در یک سیستم فروشگاهی میخواهیم هر ۳۰ ثانیه سفارشهای Pending را بررسی کنیم:
public sealed class OrderWorker(
IServiceScopeFactory scopeFactory)
: BackgroundService
{
protected override async Task ExecuteAsync(
CancellationToken stoppingToken)
{
using var timer = new PeriodicTimer(
TimeSpan.FromSeconds(30));
while (await timer.WaitForNextTickAsync(
stoppingToken))
{
using var scope =
scopeFactory.CreateScope();
var service =
scope.ServiceProvider
.GetRequiredService<IOrderService>();
await service.ProcessPendingOrdersAsync(
stoppingToken);
}
}
}
اینجا Flow کاملاً مشخص است
و وقتی Application در حال Shutdown باشد، CancellationToken میتواند انتظار Timer را متوقف کند.
📌 پس PeriodicTimer برای چه چیزی مناسب است؟
✅ اجرای Periodic یک عملیات درون یک Process
✅ ءBackground Workerهای ساده
✅ ءPolling
✅ ءCache Refresh
✅ بررسی دورهای وضعیت
✅ ءCleanupهای ساده
✅ ءSync دورهای
و مخصوصاً زمانی که میخواهید این منطق را بهشکل async و قابل Cancellation بنویسید.
#تصمیمهای_مهندسی | Engineering Decisions
یه زمانی فکر میکردم هر چیزی که دوباره استفاده میشه، باید سریع تبدیلش کنیم به یک Abstraction.
مثلاً دو تا Service داشتیم که تقریباً کار مشابهی انجام میدادن.سریع میگفتیم:
«اینارو Generic کنیم که Duplicate Code نداشته باشیم.»
یک Interface. یک Base Class. چندتا Generic Method. چندتا Strategy. و تمام. 😅
روی کاغذ خیلی تمیز به نظر میرسید.
تا چند ماه بعد که Requirement یکی از اون Serviceها تغییر کرد. حالا باید یک رفتار خاص بهش اضافه میکردیم.
ولی چون با یک Abstraction مشترک بسته شده بود، تغییر ساده تبدیل شد به کلی شرط و Exception.
آخرش هم فهمیدیم:
ما دو چیز مشابه را فقط چون امروز شبیه هم بودند، یکی کرده بودیم.
در حالی که ممکن بود فردا مسیرشان کاملاً از هم جدا شود.
از اون موقع یک قانون ساده برای خودم دارم:
ءDuplicate Code همیشه دشمن ما نیست.
گاهی چند خط کد تکراری، از یک Abstraction اشتباه خیلی ارزانتره.
اول اجازه میدم الگو خودش را نشان بده.
بعد اگر واقعاً فهمیدیم این دو رفتار یک مفهوم مشترک دارند،Abstraction میسازیم.
نه صرفاً برای اینکه چند خط Code کمتر داشته باشیم.
چون هدف Refactoring این نیست که Code کمتر شود.
هدف اینه که مدل ذهنی ما از سیستم درستتر شود.
گاهی بهترین Abstraction...
همون Abstractionیه که هنوز نساختیم.
یه زمانی فکر میکردم هر چیزی که دوباره استفاده میشه، باید سریع تبدیلش کنیم به یک Abstraction.
مثلاً دو تا Service داشتیم که تقریباً کار مشابهی انجام میدادن.سریع میگفتیم:
«اینارو Generic کنیم که Duplicate Code نداشته باشیم.»
یک Interface. یک Base Class. چندتا Generic Method. چندتا Strategy. و تمام. 😅
روی کاغذ خیلی تمیز به نظر میرسید.
تا چند ماه بعد که Requirement یکی از اون Serviceها تغییر کرد. حالا باید یک رفتار خاص بهش اضافه میکردیم.
ولی چون با یک Abstraction مشترک بسته شده بود، تغییر ساده تبدیل شد به کلی شرط و Exception.
آخرش هم فهمیدیم:
ما دو چیز مشابه را فقط چون امروز شبیه هم بودند، یکی کرده بودیم.
در حالی که ممکن بود فردا مسیرشان کاملاً از هم جدا شود.
از اون موقع یک قانون ساده برای خودم دارم:
ءDuplicate Code همیشه دشمن ما نیست.
گاهی چند خط کد تکراری، از یک Abstraction اشتباه خیلی ارزانتره.
اول اجازه میدم الگو خودش را نشان بده.
بعد اگر واقعاً فهمیدیم این دو رفتار یک مفهوم مشترک دارند،Abstraction میسازیم.
نه صرفاً برای اینکه چند خط Code کمتر داشته باشیم.
چون هدف Refactoring این نیست که Code کمتر شود.
هدف اینه که مدل ذهنی ما از سیستم درستتر شود.
گاهی بهترین Abstraction...
همون Abstractionیه که هنوز نساختیم.
📌 200+ سوال مصاحبه واقعی (بخش 2️⃣)
[ BUCKET 1 — C# AND .NET ]
⚠️ فصل ۱۱ — مدیریت خطا در #C (۳ سؤال)
5️⃣3️⃣ تفاوت بین throw ex و throw داخل یک catch block چیست؟ ⚠️
6️⃣3️⃣ هزینهی Throw کردن Exception در NET. چقدر است و چرا نباید از Exceptionها برای Control Flow استفاده کنیم؟ 🚨
7️⃣3️⃣ وقتی یک Exception رخ میدهد، CLR چگونه Stack را Unwind میکند؟ و JIT در مرزهای try/catch چه کاری انجام میدهد؟ 🧠⚙️
⚡️ فصل ۱۲ — Async/Await و Taskها (۶ سؤال)
8️⃣3️⃣ تفاوت بین Task و ValueTask چیست و هرکدام را چه زمانی باید استفاده کنیم؟ ⚡️
9️⃣3️⃣ هدف ConfigureAwait(false) چیست و چه زمانی باید از آن استفاده کنیم؟ 🔄
0️⃣4️⃣ ءCompiler برای یک متد async چه State Machineای تولید میکند؟ این State Machine چه چیزهایی را Allocate میکند؟ 🧠
1️⃣4️⃣ ءSynchronizationContext چیست و چگونه روی Resume شدن یک await تأثیر میگذارد؟ 🔄
2️⃣4️⃣ تفاوت async void و async Task چیست و چرا async void خطرناک است؟ ⚠️
3️⃣4️⃣ چرا وقتی در Contextی که SynchronizationContext دارد روی یک Task از .Result یا .Wait() استفاده میکنیم، ممکن است Deadlock رخ دهد؟ 🔒
🧵 فصل ۱۳ — Threading و Synchronization (۴ سؤال)
4️⃣4️⃣ ءlock چیست و به چه چیزی Compile میشود؟ 🔐
5️⃣4️⃣ شیء جدید Lock در C# 13 چیست و چگونه نسبت به Lock کردن روی یک Object دلخواه بهتر عمل میکند؟ 🆕🔒
6️⃣4️⃣ ء<Channel<T در NET. چیست و در مقایسه با <BlockingCollection<T چه سناریوهایی را حل میکند؟ 📡
7️⃣4️⃣ وقتی Threadهای ThreadPool به دلیل sync-over-async یا Blocking روی عملیات I/O دچار Starvation شوند چه اتفاقی میافتد؟ چگونه آن را Diagnose میکنید؟ 🚨🧵
🧠 فصل ۱۴ — مدیریت حافظه و GC (۵ سؤال)
8️⃣4️⃣ ءGenerationهای 0، 1 و 2 در GC را توضیح دهید. Objectها چگونه بین این Generationها Promote میشوند؟ ♻️
9️⃣4️⃣ ءLarge Object Heap (LOH) چیست و چرا برای Performance اهمیت دارد؟ 🧠📦
0️⃣5️⃣ تفاوت Workstation GC و Server GC چیست؟ 🖥⚙️
1️⃣5️⃣ ءDispose Pattern چیست و چرا شامل Dispose(bool disposing) است؟ 🗑
2️⃣5️⃣ چگونه یک Memory Leak را در یک برنامهی NET. پیدا و Diagnose میکنید؟ از چه ابزارهایی استفاده میکنید؟ 🔍🧠
🔎 فصل ۱۵ — Reflection و Dynamic (۲ سؤال)
3️⃣5️⃣ هزینهی Performance مربوط به Reflection چیست و چگونه میتوان نتایج Reflection را Cache کرد؟ 🔍⚡️
4️⃣5️⃣ ءSource Generatorها در مقایسه با Reflection برای حل مسائل مشابه چه تفاوتی دارند؟ ⚙️
📐 فصل ۱۶ — Span، Memory و Pipelines (۳ سؤال)
5️⃣5️⃣ ء<Span<T چیست و چه مشکلی را حل میکند؟ 🚀
6️⃣5️⃣ چرا <Span<T نمیتواند بهعنوان یک Field در یک Class معمولی استفاده شود؟ راهکار چیست؟ 🧠
7️⃣5️⃣ ء<ArrayPool<T چیست و چه زمانی باید از آن استفاده کنید؟ ♻️📦
📦 فصل ۱۷ — Serialization (۲ سؤال)
8️⃣5️⃣ حالت Source Generator در System.Text.Json چیست و چه زمانی باید از آن استفاده کنیم؟ ⚡️
9️⃣5️⃣ چرا BinaryFormatter Deprecated شد و بهجای آن باید از چه چیزی استفاده کنیم؟ 🚫
🆕 فصل ۱۸ — قابلیتهای نسخههای #C (۲ سؤال)
0️⃣6️⃣ کلیدواژهی field در C# 14 چیست و چه مشکلی را در Setterهای Property حل میکند؟ 🆕✨
1️⃣6️⃣ ءCallerArgumentExpression چیست و چگونه در پیادهسازی Guard Clauseها کاربرد دارد؟ 🛡
⚙️ فصل ۱۹ — NET Runtime، CLR، JIT. و AOT (۳ سؤال)
6️⃣2️⃣ ءTiered Compilation در NET. چیست و چگونه بین Startup Performance و Steady-State Performance تعادل ایجاد میکند؟ 🚀
6️⃣3️⃣ ءNative AOT چیست و استفاده از آن چه Trade-offهایی دارد؟ ⚡️
6️⃣4️⃣ ءPGO (Profile-Guided Optimization) چیست و Dynamic PGO در NET. چگونه کار میکند؟ 🧠🚀
📊 فصل ۲۰ — Performance و Benchmarking (۲ سؤال)
6️⃣5️⃣ چرا نباید از Stopwatch و یک حلقهی for بهعنوان جایگزین BenchmarkDotNet استفاده کنیم؟ ⏱️
6️⃣6️⃣ ءMemoryDiagnoser چه چیزی را اندازهگیری میکند و چگونه باید تعداد Gen0، Gen1 و Gen2 را تفسیر کنیم؟ 🧠📊
📁 فصل ۲۱ — BCL و I/O (۲ سؤال)
6️⃣7️⃣ تفاوت بین Stream، MemoryStream و FileStream چیست؟ 📁
6️⃣8️⃣ ءRandomAccess که در NET 6. معرفی شد چیست و چه سناریویی را بهبود میدهد؟ ⚡️📂
📦 فصل ۲۲ — NuGet (۱ سؤال)
6️⃣9️⃣ ءCentral Package Management و فایل Directory.Packages.props چیستند؟ 📦
🛠 فصل ۲۳ — MSBuild و csproj (۲ سؤال)
7️⃣0️⃣ ءDirectory.Build.props و Directory.Build.targets چیستند و چه کاربردی دارند؟ 🛠
7️⃣1️⃣ ءMulti-Targeting چیست و چه زمانی از آن استفاده میکنید؟ 🎯
💻 فصل ۲۴ — NET CLI. (۲ سؤال)
7️⃣2️⃣ تفاوت dotnet build و dotnet publish چیست؟ 💻
7️⃣3️⃣ ءglobal.json چیست و چه مشکلی را در محیطهایی که چند نسخهی NET. دارند حل میکند؟ ⚙️
👨💻 دوستان وقتشه یه Community بسازیم! 🚀
از شما میخوام که بیاید توی گروه چت کنار هم باشیم؛ سؤال بپرسیم، تجربههامون رو به اشتراک بذاریم و درباره هر چیزی که به دنیای NET. مربوطه بحث کنیم و از تجربه همدیگه استفاده کنیم. 🔥
اینجا قرار نیست فقط چندتا پیام رد و بدل بشه؛ هدفمون ساختن یه Community فعال از مهندسهای NET. که هر کسی چیزی برای یاد دادن و یاد گرفتن داشته باشه. 🤝
اگه تجربهای دارید، حتماً به درد یکی دیگه میخوره.
پس بیاید کنار هم یه کامیونیتی خفن بسازیم. ❤️🔥
📌منتظرتونیم: [ C# Geeks (Chat) ]
از شما میخوام که بیاید توی گروه چت کنار هم باشیم؛ سؤال بپرسیم، تجربههامون رو به اشتراک بذاریم و درباره هر چیزی که به دنیای NET. مربوطه بحث کنیم و از تجربه همدیگه استفاده کنیم. 🔥
اینجا قرار نیست فقط چندتا پیام رد و بدل بشه؛ هدفمون ساختن یه Community فعال از مهندسهای NET. که هر کسی چیزی برای یاد دادن و یاد گرفتن داشته باشه. 🤝
اگه تجربهای دارید، حتماً به درد یکی دیگه میخوره.
پس بیاید کنار هم یه کامیونیتی خفن بسازیم. ❤️🔥
📌منتظرتونیم: [ C# Geeks (Chat) ]
C# Geeks (.NET) pinned «👨💻 دوستان وقتشه یه Community بسازیم! 🚀 از شما میخوام که بیاید توی گروه چت کنار هم باشیم؛ سؤال بپرسیم، تجربههامون رو به اشتراک بذاریم و درباره هر چیزی که به دنیای NET. مربوطه بحث کنیم و از تجربه همدیگه استفاده کنیم. 🔥 اینجا قرار نیست فقط چندتا پیام رد و بدل…»
📌 200+ سوال مصاحبه واقعی (بخش 3️⃣)
[ BUCKET 2 - ASP.NET Core AND EF Core ]
🌐 فصل 25 — انواع پروژههای وب در ASP.NET Core
📌 76. انواع اصلی پروژههای وب در ASP.NET Core چیستند؟
📌 77. چه زمانی Minimal APIs را بهجای یک پروژهی API مبتنی بر Controller انتخاب میکنید؟
🏠 فصل 26 — Application Host
⚙️ 78. ءWebApplicationBuilder چیست و چگونه پیکربندی Host و Application را سادهتر میکند؟
🔄 79. ءIHostedService چیست و یک کاربرد معمول آن چیست؟
🛑 80. ءIHost.StopAsync از چه مکانیزمهایی برای ارسال سیگنال Graceful Shutdown استفاده میکند؟
🎮 فصل 27 — Controllers
🔗 81. ءModel Binding چیست و در ASP.NET Core Controllers چگونه کار میکند؟
📥 82. تفاوت بین [FromBody]، [FromQuery]، [FromRoute] و [FromForm] چیست؟
🎯 83. هدف IActionResult چیست و چرا ممکن است آن را به یک Concrete Return Type ترجیح دهیم؟
♻️ 84. چرخهی حیات یک Controller در ASP.NET Core چگونه است؟ چه زمانی ساخته و چه زمانی Dispose میشود؟
🚨 85. چگونه میتوان Exceptionها را بهصورت سراسری برای تمام Controllerها مدیریت کرد؟
⚡️ فصل 28 — Minimal APIs
🔹 86. ءMinimal API چه تفاوتی با یک API سنتی مبتنی بر Controller دارد؟
🔐 87. چگونه Endpointهای Minimal API را با استفاده از Authentication و Authorization امن میکنید؟
🔄 88. چگونه در Minimal APIs، API Versioning را پیادهسازی میکنید؟
🧩 89. چه نوع Filterهایی توسط Minimal APIs پشتیبانی میشوند؟
🏢 90. استفاده از Minimal APIs در یک Application بزرگ و Enterprise چه مزایا و معایبی دارد؟
🔗 فصل 29 — Middlewareها
⛓️ 91. ترتیب اجرای Middlewareها چگونه است و چرا اهمیت دارد؟
🛠 92. تمام روشهای ایجاد Middleware در ASP.NET Core را نام ببرید.
⚙️ 93. تفاوت بین Convention-Based Middleware و Factory-Based Middleware چیست؟
🚨 94. چگونه میتوان Exceptionها را بهصورت سراسری با استفاده از Middleware مدیریت کرد؟
🎯 فصل 30 — Filterها
🧩 95. انواع اصلی Filterهای موجود در ASP.NET Core را نام ببرید.
🔢 96. چگونه میتوان ترتیب اجرای Filterها را کنترل کرد؟
⚙️ 97. تفاوت بین Resource Filter و Action Filter چیست؟
🌐 98. منظور از Filter Scopeهای Global، Controller و Action چیست و اولویت اجرای آنها چگونه تعیین میشود؟
🌍 فصل 31 — مبانی REST
📈 99. ءRichardson Maturity Model چیست و چه سطوحی دارد؟
🏗 100. شش Architectural Constraint در REST را نام ببرید و هرکدام را بهطور خلاصه توضیح دهید.
🔄 101. تفاوت بین PUT و PATCH چیست؟
🎯 102. ءIdempotency چیست؟ کدام HTTP Methodها Idempotent هستند؟
🔐 103. تفاوت بین 401 Unauthorized و 403 Forbidden چیست؟
🔗 104. ءHATEOAS چیست و چه ارتباطی با REST Level 3 دارد؟
📄 105. استراتژیهای رایج برای Pagination در REST APIها چیستند؟
🎛 106. ءData Shaping در REST APIها چیست و چرا مفید است؟
🔄 فصل 32 — API Versioning
🌐 107. تفاوت بین روشهای Versioning مبتنی بر URL، Query String، Header و Content Negotiation چیست؟
⚠️ 108. چگونه یک API Version را بهعنوان Deprecated علامتگذاری میکنید؟
🛡 109. هنگام معرفی یک API Version جدید، چگونه Backward Compatibility را حفظ میکنید؟
✅ فصل 33 — Validation
🧪 110. ءFluentValidation چیست و چرا ممکن است آن را به DataAnnotations ترجیح دهید؟
🌳 111. ءFluentValidation چگونه Complex Object Graphها یا Child Collectionها را اعتبارسنجی میکند؟
⏳ 112. چگونه در FluentValidation، Asynchronous Validation انجام میدهید؟
💉 113. چگونه میتوان سرویسهایی مانند دسترسی به Database را داخل یک FluentValidation Validator تزریق کرد؟
🚨 114. چگونه خطاهای FluentValidation را در APIها به شکل Problem Details برمیگردانید؟
🗺 فصل 34 — Mapping
🔄 115. ءAutoMapper چیست و در Solutionهای بزرگ چه مشکلات و اشتباهات رایجی ممکن است ایجاد کند؟
⚡️ 116. ءMapperly چیست و چگونه از Source Generatorها استفاده میکند؟
⚖️ 117. استفاده از Mapperly در مقایسه با Mapperهای Runtime مانند AutoMapper چه مزایا و معایبی دارد؟
🧑💻 118. چه زمانی Manual Mapping میتواند انتخاب بهتری نسبت به استفاده از یک Mapping Library باشد؟
یه اتفاق جالب با AI داره توی تیم های Software میافته.
قبلاً میگفتیم:
«این Code رو کی نوشته؟»
الان باید یه سوال دیگه هم بپرسیم:
«کی واقعاً میفهمتش؟»
قبلاً میگفتیم:
«این Code رو کی نوشته؟»
الان باید یه سوال دیگه هم بپرسیم:
«کی واقعاً میفهمتش؟»
#Engineering_Productivity
یه روز تصمیم گرفتم ببینم واقعاً چرا بعضی روزها ۸ ساعت کار میکنم، ولی آخر روز حس میکنم تقریباً هیچ کاری نکردم.
شروع کردم به نگاه کردن به کارهایی که اون روز انجام داده بودم:
۳۰ دقیقه روی یک Bug.
بعد یک پیام از تیم.
رفتم سراغ یک PR.
بعد یک سؤال از Product.
برگشتم روی Bug.
یک Meeting.
بعد CI شکست.
بعد دوباره PR.
آخر روز شاید ۶-۷ ساعت درگیر کار بودم...
ولی هیچکدوم واقعاً جلو نرفته بود.
مشکل کمکاری نبود. Context Switching بود.
هر بار که از یک مسئله خارج میشیم و وارد مسئلهی دیگری میشیم، بخشی از Context قبلی رو از دست میدیم.
و وقتی دوباره برمیگردیم، باید زمان بذاریم تا یادمون بیاد:
«کجا بودم؟»
«چی داشتم بررسی میکردم؟»
«چرا این تصمیم رو گرفتم؟»
برای همین گاهی:
حتی بدتر...
اگر چند نفر از یک تیم دائماً همدیگه رو Interrupt کنن، مشکل فقط فردی نیست.
کل تیم وارد یک چرخه میشه:
برای همین بعضی تیمها با اضافه کردن آدم بیشتر، الزاماً سریعتر نمیشن.
چون ممکنه فقط تعداد Interruptها رو بیشتر کنن.
این روزها سعی میکنم هر کاری که نیاز به تمرکز داره رو تا جای ممکن یکتکه انجام بدم.Notification کمتر. Meeting کمتر. Taskهای همزمان کمتر.
و مهمتر از همه:
کارهای نیمهتمام کمتر.
چون Productivity همیشه یعنی سریعتر کار کردن نیست.
گاهی یعنی:
اجازه بدی یک Engineer آنقدر Context داشته باشه که واقعاً یک مسئله رو تمام کنه.
یه روز تصمیم گرفتم ببینم واقعاً چرا بعضی روزها ۸ ساعت کار میکنم، ولی آخر روز حس میکنم تقریباً هیچ کاری نکردم.
شروع کردم به نگاه کردن به کارهایی که اون روز انجام داده بودم:
۳۰ دقیقه روی یک Bug.
بعد یک پیام از تیم.
رفتم سراغ یک PR.
بعد یک سؤال از Product.
برگشتم روی Bug.
یک Meeting.
بعد CI شکست.
بعد دوباره PR.
آخر روز شاید ۶-۷ ساعت درگیر کار بودم...
ولی هیچکدوم واقعاً جلو نرفته بود.
مشکل کمکاری نبود. Context Switching بود.
هر بار که از یک مسئله خارج میشیم و وارد مسئلهی دیگری میشیم، بخشی از Context قبلی رو از دست میدیم.
و وقتی دوباره برمیگردیم، باید زمان بذاریم تا یادمون بیاد:
«کجا بودم؟»
«چی داشتم بررسی میکردم؟»
«چرا این تصمیم رو گرفتم؟»
برای همین گاهی:
8 Hours Worked
≠
8 Hours of Progress
حتی بدتر...
اگر چند نفر از یک تیم دائماً همدیگه رو Interrupt کنن، مشکل فقط فردی نیست.
کل تیم وارد یک چرخه میشه:
Work
↓
Interrupt
↓
Context Switch
↓
Recovery
↓
Work
↓
Interrupt
برای همین بعضی تیمها با اضافه کردن آدم بیشتر، الزاماً سریعتر نمیشن.
چون ممکنه فقط تعداد Interruptها رو بیشتر کنن.
این روزها سعی میکنم هر کاری که نیاز به تمرکز داره رو تا جای ممکن یکتکه انجام بدم.Notification کمتر. Meeting کمتر. Taskهای همزمان کمتر.
و مهمتر از همه:
کارهای نیمهتمام کمتر.
چون Productivity همیشه یعنی سریعتر کار کردن نیست.
گاهی یعنی:
اجازه بدی یک Engineer آنقدر Context داشته باشه که واقعاً یک مسئله رو تمام کنه.
📌 200+ سوال مصاحبه واقعی (بخش 4️⃣)
[ BUCKET 2 - ASP.NET Core AND EF Core ]
🔧 فصل 35 — Configuration و Options Pattern
📌 119. ءOptions Pattern چیست و چرا استفاده از آن بهجای دسترسی مستقیم به Configuration ترجیح داده میشود؟
🔄 120. تفاوت بین IOptions<T>، IOptionsSnapshot<T و <IOptionsMonitor<T چیست؟
✅ 121. چگونه Configurationای را که به یک Class متصل شده است، با استفاده از DataAnnotations اعتبارسنجی میکنید؟
🔐 122. چگونه مقادیر حساس Configuration مانند API Keyها یا Connection Stringها را امن نگه میدارید؟
🚨 فصل 36 — Error Handling
🛑 123. ءMiddleware مربوط به UseExceptionHandler چیست و چگونه آن را پیکربندی میکنید؟
🆕 124. در NET 8. برای مدیریت سراسری خطاها چه قابلیت جدیدی با نام IExceptionHandler اضافه شده است؟
📋 125. چگونه در ASP.NET Core APIها، Problem Details (RFC 9457) را برمیگردانید؟
🔀 126. چگونه Exceptionهای سفارشی را بهصورت سراسری به HTTP Status Codeهای مشخص نگاشت میکنید؟
📝 فصل 37 — Logging
🔍 127. ءStructured Logging چیست و چرا باید از آن استفاده کنیم؟
🏷 128. ءScopeها در ASP.NET Core Logging چیستند و چگونه از آنها استفاده میکنید؟
🗄 129. چگونه Logging مربوط به Database Commandهای EF Core را فعال میکنید؟
📊 130. چگونه برای محیط Production، Log Aggregation و Monitoring را پیادهسازی میکنید؟
🔄 131. چگونه در Serilog حجم Logها و Log File Rotation را کنترل میکنید؟
📌 فصل 38 — Health Checks
🟢 132. تفاوت بین Liveness Probe و Readiness Probe چیست؟
🩺 133. چگونه یک Custom Health Check پیادهسازی میکنید؟
💉 فصل 39 — Dependency Injection (DI)
🔄 134. سه DI Lifetime رایج در ASP.NET Core کداماند؟
⚠️ 135. هنگام Inject کردن یک Scoped Service داخل یک Singleton Service چه مشکلاتی ممکن است ایجاد شود؟
🔧 136. چگونه یک Scoped Service را داخل یک Background Task یا Singleton Class Resolve میکنید؟
🔄 137. چگونه با Circular Dependencyها در DI برخورد میکنید؟
🎯 138. چگونه Conditional Dependency Resolution را پیادهسازی میکنید؛ مثلاً بر اساس Configuration؟
🗄 فصل 40 — Entity Framework Core
📦 139. تفاوت بین DbContext و DbSet چیست؟
🔗 140. تفاوت بین Eager Loading، Lazy Loading و Explicit Loading چیست؟
⚡️ 141. چه زمانی و چرا باید از ( )AsNoTracking. در Queryها استفاده کنید؟
📊 142. ءQuery Splitting (AsSplitQuery) چیست و چه مشکل Performanceای را حل میکند؟
♻️ 143. ءContext Pooling چیست و چگونه به Applicationهای با Throughput بالا کمک میکند؟
🔒 144. چگونه Optimistic Concurrency را با استفاده از RowVersion یا Concurrency Token پیادهسازی و مدیریت میکنید؟
⚡️ 145. چگونه عملیات Batch Update/Delete را در EF Core بدون Load کردن Entityها انجام میدهید؟
[ BUCKET 3 — CACHING, SCHEDULING, MESSAGING, AND SECURITY ]
🔐 فصل 41 — Authentication و Authorization
🆔 146. تفاوت بین Authentication و Authorization چیست؟
🍪 147. تفاوت بین Cookie Authentication و JWT Bearer Authentication چیست؟
📌 148. ءRefresh Token چیست؟ جریان Authentication با JWT + Refresh Token را توضیح دهید.
🛡 149. چگونه Policy-Based یا Claim-Based Authorization را پیادهسازی میکنید؟
📋 150. در مفهوم Authorization Policy، یک Requirement چیست؟
⚙️ 151. چگونه یک Custom Authorization Handler ایجاد میکنید؟
🔄 152. یک سناریو را توضیح دهید که در آن Claims Transformation ضروری است و نحوه پیادهسازی آن را بیان کنید.
👤 فصل 42 — ASP.NET Core Identity
🔑 153. چگونه قوانین Password مانند طول، پیچیدگی و سایر الزامات را در Identity پیکربندی میکنید؟
📱 154. چگونه با استفاده از Identity، Two-Factor Authentication (2FA) را پیادهسازی میکنید؟
🔒 155. چگونه میتوان یک User را پس از چند Login ناموفق Lockout کرد؟
🔗 156. چگونه Identity را با Authentication مبتنی بر JWT Token برای APIها یکپارچه میکنید؟
🗄 157. چگونه برای یک Database از نوع NoSQL، Custom User Store ایجاد و مدیریت میکنید؟
🛡 فصل 43 — ASP.NET Core Security
🌐 158. ءCORS چیست و اجازه دادن به دسترسی گسترده از طریق آن چه خطرات امنیتی دارد؟
✈️ 159. درخواستهای Preflight OPTIONS در CORS چگونه کار میکنند؟
🔐 160. چگونه Signature و Claims یک JWT را اعتبارسنجی میکنید؟
⚠️ 161. ءRefresh Tokenها چه خطرات امنیتیای دارند؟
🚫 162. چگونه Refresh Tokenها را مثلاً هنگام Logout یا تغییر Password Invalidate میکنید؟
🔑 163. ءOAuth 2.0 چیست و چگونه برای امنسازی APIها استفاده میشود؟
🆔 164. ءOpenID Connect (OIDC) چیست و چه تفاوتی با OAuth 2.0 دارد؟
📌 ءPagination رو باOffsetپیاده کنیم یاCursor؟
یکی از اون تصمیمهاییه که موقع ساختن API خیلی راحت از کنارش رد میشیم.
مثلاً میخوایم لیست سفارشها رو برگردونیم.
خب معلومه دیگه 😎
میزنیم:
SELECT *
FROM Orders
ORDER BY Id
OFFSET 100000
LIMIT 20;
یا توی EF Core:
var orders = await db.Orders
.OrderBy(x => x.Id)
.Skip(100000)
.Take(20)
.ToListAsync();
تموم شد رفت.
هم سادهست، هم خواناست، هم برای صفحهبندی خیلی راحت میتونیم بگیم:
page=1page=2page=3و الی آخر...
ولی یک لحظه صبر کن... 🤨
واقعاً فکر میکنی وقتی رسیدیم به صفحهی مثلاً 10,000، دیتابیس میگه:
«چشم قربان، دقیقاً 20 تا رکوردت رو از این وسط برمیدارم»؟ 😎
نه دقیقاً!
OFFSET به دیتابیس میگه:«این 100 هزار رکورد اول رو رد کن، بعد 20 تای بعدی رو بده.»
یعنی رکوردهایی که قرار نیست به Application برسن، باز هم باید در سمت دیتابیس پردازش بشن.
خود PostgreSQL هم صراحتاً میگه رکوردهایی که توسط
OFFSET رد میشن، همچنان باید توسط Server محاسبه بشن و OFFSETهای بزرگ میتونن inefficient باشن.پس اگر دیتاست کوچیکه؟
احتمالاً اصلاً مسئلهی خاصی نداری.
ولی اگر داری با میلیونها رکورد و Pagination عمیق سروکله میزنی، داستان فرق میکنه.
حالا بریم سراغ گزینهی دوم:
🎯 Cursor / Keyset Pagination
اینجا بهجای اینکه به دیتابیس بگیم:
«100 هزار تا رکورد رو رد کن»
میگیم:
«من تا اینجا اومدم؛ از بعدِ این رکورد ادامه بده.»
مثلاً:
SELECT *
FROM Orders
WHERE Id > 100000
ORDER BY Id
LIMIT 20;
یا در EF Core:
var orders = await db.Orders
.Where(x => x.Id > lastSeenId)
.OrderBy(x => x.Id)
.Take(20)
.ToListAsync();
اینجا دیگه مفهوم اصلی
page number نیست.مفهوم اصلی اینه:
🧠 من آخرین چیزی که دیدم چی بود؟
مثلاً Response اول:
{
"items": [...],
"nextCursor": "100020"
}Request بعدی:
GET /orders?cursor=100020
و دیتابیس میگه:
WHERE Id > 100020
ORDER BY Id
LIMIT 20
اگر روی
Id ایندکس داشته باشیم، دیتابیس میتونه خیلی مستقیمتر به محدودهی موردنظر برسه.ءMicrosoft هم در مستندات EF Core، برای Paginationهایی که فقط حرکت صفحهبهصفحه لازم دارند، Keyset Pagination را بهعنوان جایگزین مناسب
Skip/Take معرفی میکند.اما داستان فقط Performance نیست! 👀
فرض کن کاربر صفحهی 2 رو گرفته.
بعد وسط کار، یک Order جدید وارد سیستم میشه.
اگر از
OFFSET استفاده کنیم، موقع درخواست صفحهی بعدی ممکنه مجموعهی نتایج نسبت به درخواست قبلی جابهجا شده باشه و در شرایط تغییر همزمان دادهها، بعضی رکوردها دوباره دیده بشن یا بعضیها از دست برن.ولی Keyset میگه:
«من آخرین رکوردی که دیدم رو میدونم؛ از همون نقطه ادامه بده.»
به همین دلیل برای چیزهایی مثل:
📰 Feed
🛒 Order List
💬 Message List
📜 Activity Log
🔔 Notification List
و سیستمهایی که کاربر معمولاً فقط میخواد:
Next → Next → Nextبره جلو، Cursor Pagination خیلی جذاب میشه.
اماااااا... 😏
اینجا هم قرار نیست بگیم: Cursor > Offset
و تمام!
چون Cursor یک محدودیت مهم داره.
فرض کن کاربر میگه:
«برو صفحه 873!»
با Cursor این کار به اون سادگی Offset نیست.
چون Cursor اساساً برای حرکت ترتیبی روی یک Result Set طراحی شده، نه Jump کردن مستقیم به یک Page Number.
پس مثلاً برای یک: 👨💼 Admin Panel
که کاربر میخواد بگه:
Page 1 | 2 | 3 | ... | 50و مستقیماً بره صفحه 30...
Offset Paginationهنوز میتونه انتخاب کاملاً معقولی باشه.
اما برای یک: 📱 Infinite Scroll
که کاربر فقط میگه:
«بیشتر بیار» Cursor معمولاً انتخاب بهتریه.
پس اگر بخوام خیلی خلاصه تصمیم بگیرم:
🟢 Offset Pagination
وقتی:
▫️تعداد داده خیلی زیاد نیست
▫️کاربر باید مستقیماً به یک Page خاص بره
▫️ءUX بر اساس Page Number طراحی شده
▫️سادگی Implementation برات مهمه
🔵 Cursor / Keyset Pagination
وقتی:
▫️ءDataset بزرگه
▫️ءPagination عمیقه
▫️کاربر معمولاً Next/Previous میکنه
▫️ءInfinite Scroll داری
▫️دادهها مرتباً Insert/Delete میشن
▫️ءPerformance در صفحات عمیق مهمه
و یک نکتهی خیلی مهم:
اگر Pagination داری، Order باید deterministic و ترجیحاً unique باشه.
مثلاً فقط:
.OrderByDescending(x => x.CreatedAt)
ممکنه کافی نباشه، چون چند رکورد میتونن
CreatedAt یکسان داشته باشن.بهتره مثلاً:
.OrderByDescending(x => x.CreatedAt)
.ThenByDescending(x => x.Id)
داشته باشی تا ترتیب کاملاً مشخص باشه. Microsoft هم روی unique بودن ترتیب برای Pagination تأکید کرده.
🔖هشتگها:
#Pagination #OffsetPagination #CursorPagination #KeysetPagination
Forwarded from thisisnabi.dev [10x Developer]
سلام عزیزان
کمی بخاطر سر شلوغی این موضوع عقب افتاد اما می تونید جزئیات میت ها رو در سایت زیر ببینید.
https://thisisnabi.dev
برای پرداخت 10x developer همون طور که قول دادم 50% تخفیف بگیرید.
اما چون system design هنوز پیش ثبت نامه شرایط پرداخت 4 قسطه اسنپ پی رو اگر دارین می تونید باهاش پرداخت انجام بدید.
برای جزئیات پرداخت به @thisisnabi_admin پیام بدید.
کمی بخاطر سر شلوغی این موضوع عقب افتاد اما می تونید جزئیات میت ها رو در سایت زیر ببینید.
https://thisisnabi.dev
برای پرداخت 10x developer همون طور که قول دادم 50% تخفیف بگیرید.
اما چون system design هنوز پیش ثبت نامه شرایط پرداخت 4 قسطه اسنپ پی رو اگر دارین می تونید باهاش پرداخت انجام بدید.
برای جزئیات پرداخت به @thisisnabi_admin پیام بدید.
📌 200+ سوال مصاحبه واقعی (بخش 5️⃣)
[ BUCKET 3 — CACHING, SCHEDULING, MESSAGING, AND SECURITY ]
⚡️ فصل 44 — Caching
💾 165. ءIDistributedCache چیست و چه تفاوتی با Memory Cache دارد؟
⏳ 166. چگونه Cache Expiration و Eviction Policyها را برای Redis پیادهسازی میکنید؟
🚀 167.ءOutputCache در ASP.NET Core چیست و چگونه آن را برای Endpointها پیکربندی میکنید؟
🎯 168. چگونه میتوان OutputCache را بر اساس پارامترهای Request یا هویت کاربر (User Identity) تغییر داد؟
🔀 169. ءHybridCache چیست و چگونه In-Memory Cache و Distributed Cache را با یکدیگر ترکیب میکند؟
🏗 170. چگونه از HybridCache با یک استراتژی L1/L2 در ASP.NET Core استفاده میکنید؟
🔥 171. ءFusionCache چیست و چه مشکلی را که HybridCache دارد، حل میکند؟
⏰ فصل 49 — Task Scheduling
⚙️ 172. ءBackground Service در ASP.NET Core چیست؟ چه زمانی Start و چه زمانی Stop میشود؟
🔄 173. تفاوت بین Background Service و IHostedService چیست؟
🚨 174. اگر یک Unhandled Exception در یک Background Service رخ دهد، چه اتفاقی میافتد؟
📅 175. چگونه با استفاده از Quartz.NET و Cron Expressionها، Taskهای تکرارشونده (Recurring Tasks) را زمانبندی میکنید؟
💉 176. چگونه Dependencyها را داخل Quartz Jobها Inject میکنید؟
🛑 177. برای جلوگیری از اجرای چندباره یک Scheduled Job در چند Instance مختلف، از چه استراتژیهایی میتوانید استفاده کنید؟
🌐 178. چگونه یک راهکار Distributed Scheduling را برای اجرای Taskها روی چند Server معماری میکنید؟
📨 فصل 50 — Event Messaging
🔄 179. ءMediatR چیست و کدام Design Pattern را پیادهسازی میکند؟
📢 180. چگونه با استفاده از MediatR، Notification ارسال و Handle میکنید؟
⚠️ 181. استفاده بیش از حد (Overuse) از MediatR در یک پروژه چه معایب و مشکلاتی میتواند ایجاد کند؟
📨 182. ءMassTransit چیست و چه نقشی در Event-Driven Architecture دارد؟
🐇 183. چگونه MassTransit را با RabbitMQ یا یک Message Broker دیگر پیکربندی میکنید؟
📦 184. چگونه Messageها (Event / Command) را با استفاده از MassTransit تعریف و Consume میکنید؟
🚀 185. قابلیتهای پیشرفته MassTransit مانند Sagaها یا Message Retry Policyها چیستند؟
[ BUCKET 4 — SYSTEM DESIGN AND ARCHITECTURE ]
🌐 فصل 51 — APIs، SDKs و Resilience
⚠️ 186. اگر برای هر API Call یک HttpClient جدید ایجاد کنید، چه اتفاقی میافتد؟
🏭 187. ءHttpClientFactory چیست و چرا در ASP.NET Core معرفی شد؟
🎯 188. ءTyped Client چیست و چه تفاوتی با Named Client دارد؟
🔗 189. ءRefit چیست و چگونه با استفاده از آن یک API Interface تعریف میکنید؟
⛓️ 190. ءDelegatingHandler چیست و کاربردهای رایج آن چیستند؟
🛡 191. ءPolly چیست و چگونه در کنار HttpClientFactory استفاده میشود؟
🔄 192. یک مثال از اضافه کردن Retry Policy با استفاده از Polly به HttpClientFactory ارائه دهید.
🚨 193. چگونه استراتژی Circuit Breaker را با استفاده از Polly پیادهسازی میکنید؟
🛟 194. چگونه استراتژی Fallback را با استفاده از Polly پیادهسازی میکنید؟
🔐 195. چگونه Authentication و Token Refresh را برای Outgoing HTTP Requests مدیریت میکنید؟
🔭 فصل 52 — OpenTelemetry & Observability
📊 196. سه ستون اصلی (Three Pillars) مربوط به Observability در OpenTelemetry چیستند؟
🏗 197. ءOpenTelemetry Collector چیست و چه نقشی دارد؟
🔍 198. در مفهوم Distributed Tracing، یک Span و یک Trace چیستند؟
🔗 199. چگونه Distributed Context Propagation و Baggage را بین Microserviceها مدیریت میکنید؟
🎯 200. ءSampling در OpenTelemetry چیست و چرا از Strategyهای مختلف Sampling استفاده میکنید؟
📈 201. چگونه در دات نت، Custom Metrics مانند Counterها و Histogramها ایجاد میکنید؟
🔗 202. ءTrace Links در OpenTelemetry چیستند و چه زمانی مفید هستند؟
📊 203.چگونه OpenTelemetry را برای مدیریت دادههای High-Cardinality در Metricها پیکربندی میکنید؟
🐳 فصل 54 — Build & Deploy / Distributed Systems
📦 204.تفاوت بین Framework-Dependent Deployment و Self-Contained Deployment چیست؟
🎯 205.چگونه یک برنامه NET. را برای یک Runtime مشخص، مانند win-x64 یا linux-x64، Publish میکنید؟
⚙️ 206.چگونه فایلهای appsettings مخصوص هر Environment را در Publish Output پیکربندی میکنید؟
🐳 207.چگونه برای یک برنامه ASP.NET Core یک Dockerfile مینویسید؟
🏗 208.ءMulti-Stage Docker Build چیست و چرا باید از آن استفاده کنید؟
📦 209.چگونه حجم (Size) یک Docker Image را برای برنامههای NET. بهینه میکنید؟
🔐 210.چگونه Environment Variableها و Configuration را به یک Docker Container منتقل میکنید؟
📌 برایفرض کن داریم یک فروشگاه اینترنتی میسازیم وOrderفقط یکStatusداشته باشیم یا بریم سراغState Machine؟ 🤔
Orderمون چندتا وضعیت داره:Pending
Paid
Processing
Shipped
Delivered
Cancelled
خب معلومه دیگه 😎
یک
enum میسازیم:public enum OrderStatus
{
Pending,
Paid,
Processing,
Shipped,
Delivered,
Cancelled
}
بعد داخل
Order:public OrderStatus Status { get; private set; }هرجا هم خواستیم وضعیت رو عوض کنیم:
order.Status = OrderStatus.Paid;
تموم شد رفت. 😎
هم سادهست، هم خواناست، هم Database هم فقط یک ستون
Status داره.ولی یه لحظه صبر کن...
واقعاً هر
Statusای میتونه به هر Status دیگهای تبدیل بشه؟ 🤨مثلاً:
Pending → Paid
Paid → Processing
Processing → Shipped
Shipped → Delivered
اینها منطقی به نظر میرسن.
ولی این چی؟
Delivered → Pending
یا:
Shipped → Paid
یا حتی:
Cancelled → Shipped
احتمالاً نه!
پس مشکل از خود
Status نیست.مشکل اینجاست که اگر فقط یک
enum داشته باشیم، این enum بهتنهایی هیچ چیزی دربارهی قوانین انتقال بین وضعیتها نمیگه.یعنی این کد:
order.Status = OrderStatus.Delivered;
از نظر #C کاملاً معتبره.
ولی از نظر Business ممکنه کاملاً غیرمعتبر باشه.
اینجاست که معمولاً یکی میگه:
«پس قبلش if میذاریم.»
مثلاً:
if (order.Status != OrderStatus.Shipped)
throw new InvalidOperationException();
order.Status = OrderStatus.Delivered;
خب...
بعد یک ماه میشه:
if (status == OrderStatus.Pending)
{
...
}
else if (status == OrderStatus.Paid)
{
...
}
else if (status == OrderStatus.Processing)
{
...
}
بعد یک Requirement جدید میاد:
اگر Payment Failed شد، Order دوباره Payment بشه.
بعد یکی دیگه:
اگر Customer درخواست Cancellation داد، فقط قبل از Shipment اجازه بده.
بعد:
ءAdmin بتونه یک Order رو از حالت Processing به Cancelled ببره، ولی Customer نتونه.و ناگهان...💀 Business Ruleهای مربوط به Lifecycle سفارش پخش شدن توی:
Controller
Service
Handler
Domain
Background Job
...
و هرکس هم یک قانون متفاوت نوشته.
اینجا دقیقاً جاییه که State Machine میتونه ارزش پیدا کنه.
ءState Machine اساساً میگه:
«ءOrder فقط یک Status نداره؛ یک Lifecycle داره و فقط بعضی Transitionها مجاز هستند.»حالا بهجای اینکه هرجای سیستم بنویسیم:
order.Status = OrderStatus.Shipped;
میگیم:
order.Ship();
و خود Domain تصمیم میگیره آیا این Transition مجازه یا نه.
مثلاً:
public void Ship()
{
if (Status != OrderStatus.Processing)
throw new InvalidOperationException(
"Only processing orders can be shipped.");
Status = OrderStatus.Shipped;
}
حالا اگر کسی بگه:
order.Ship();
و Order هنوز
Pending باشه، خود Domain جلوش رو میگیره.این خیلی بهتر از اینه که امیدوار باشیم همهی Callerها قبلش
if درست نوشته باشن. 😏اماااا...
اینجا هم نباید سریع نتیجه بگیریم:
«پس State Machine همیشه بهتره!»
نه.
اگر سیستم ما یک Order خیلی ساده داره:
Pending → Completed
واقعاً لازم نیست برای دو وضعیت یک State Machine عظیم درست کنیم.
پس State Machine قرار نیست صرفاً چون اسمش خفنتره وارد پروژه بشه.
مسئله اینه که:
آیا Lifecycle موجودیت، خودش دارای پیچیدگی Business است؟
اینجا State Machine میتونه خیلی خواناتر و قابلکنترلتر باشه.
حتی میتونی Ruleهایی مثل این داشته باشی:
Customer فقط تا قبل از
Shippedمیتونه Cancellation درخواست کنه.
یا:
ءRefund فقط وقتی مجازه که Payment موفق بوده باشه.
یا:
ءShipment فقط بعد از Payment موفق ایجاد بشه.
اینها دیگه صرفاً
Status نیستن.اینها Business Rules مربوط به Transitionها هستن.
بعضیا فکر میکنن وقتی State Machine داریم، دیگه
Status لازم نیست.نه! State Machine و Status لزوماً رقیب هم نیستن.
🗄 10 قابلیت کمترشناختهشده SQL که هر Developer باید بداند(پارت 1️⃣)بیشتر Developerها احتمالاً فقط از حدود 20 درصد قابلیتهای SQL استفاده میکنند.
آنها
SELECT، JOIN و GROUP BY مینویسند و همانجا متوقف میشوند.اما SQL یک لایهی دوم هم دارد؛ قابلیتهایی که میتوانند یک صفحه کد Application یا سه Query جداگانه را به یک Statement تمیز و یکپارچه تبدیل کنند.⚡
ءDeveloperهای Senior همیشه به سراغ این قابلیتها میروند. بسیاری از Developerهای Junior و Mid-level حتی یکبار هم آنها را ندیدهاند.
هیچکدام از این قابلیتها جدید یا عجیبوغریب نیستند. آنها همین حالا در Databaseای که استفاده میکنید وجود دارند و منتظرند تا از آنها استفاده کنید.
🚀امروز میخواهم ۱۰ قابلیت کمترشناختهشدهی SQL را به شما نشان بدهم که هر Developer باید آنها را بشناسد.
📌در این مطلب، موارد زیر را بررسی میکنیم:
🔸️Common Table Expressions یا
CTE🔹️Window Functions
🔸️LATERAL Joins
🔹️GROUPING SETS، ROLLUP و CUBE
🔸️عبارت FILTER در Aggregateها
🔹️UPSERT با استفاده از INSERT ... ON 🔸️CONFLICT
🔹️پشتیبانی از JSON
🔸️Computed / Generated Columns
🔹️TABLESAMPLE
🔸️Partial Indexes
بریم سراغشون. 🚀
تمام Queryهای این مطلب روی Database
ء
PostgreSQL تست شدهاند. بیشتر این قابلیتها در Databaseهای دیگر نیز وجود دارند، اما Syntax دقیق آنها ممکن است متفاوت باشد؛ در طول مطلب به تفاوتهای اصلی اشاره میکنم.1️⃣ ءCommon Table Expressions یا CTE
یک Query پیچیده که همهچیز در یک Statement داخل آن فشرده شده باشد، خواندنش سخت و تغییر دادنش حتی سختتر است.
ء
Common Table Expression یا همان CTE به شما اجازه میدهد با استفاده از Keyword مربوط به WITH، آن Query را به چند مرحلهی نامگذاریشده و پشتسرهم تقسیم کنید.🧩هر مرحله مانند یک Result موقت و نامگذاریشده است که میتوانید در مراحل بعدی روی آن کار کنید.
WITH recent_shipments AS (
SELECT
s.id,
s.number,
s.carrier,
s.status,
s.created_at
FROM shipments.shipments s
WHERE s.created_at >= CURRENT_DATE - INTERVAL '30 days'
),
shipment_details AS (
SELECT
rs.number,
rs.carrier,
rs.status,
COUNT(si.id) AS total_items,
SUM(si.quantity) AS total_quantity
FROM recent_shipments rs
LEFT JOIN shipments.shipment_items si
ON rs.id = si.shipment_id
GROUP BY
rs.number,
rs.carrier,
rs.status
)
SELECT
number AS shipment_number,
carrier,
status,
total_items,
total_quantity
FROM shipment_details
ORDER BY total_quantity DESC;
این Query شامل دو بخش نامگذاریشده است.
recent_shipments، Shipmentهایی را که در 30 روز گذشته ایجاد شدهاند انتخاب میکند.📅سپس
shipment_details روی آن Result کار میکند، Itemهای مربوط به Shipment را Join میکند و تعداد و مقدار آنها را Aggregate میکند.در نهایت،
SELECT اصلی از shipment_details میخواند؛ درست مثل اینکه با یک Table معمولی کار میکند.مزیت اصلی این است که Query را میتوانید از بالا به پایین بخوانید؛ درست مثل مراحل یک دستورالعمل، بهجای اینکه مجبور باشید Nested Subqueryها را از داخل به بیرون دنبال کنید.🧠
ء
CTEها از Recursive Query نیز پشتیبانی میکنند؛ با استفاده از WITH RECURSIVE.این قابلیت زمانی بسیار کاربردی است که با دادههای Hierarchical مانند:
🏢ساختار سازمانی
🌳درخت دستهبندیها
📂ءCategory Tree
🔗ساختار Parent/Child
کار میکنید.
یک
Common Table Expression میتواند داخل Statementهای SELECT، INSERT، UPDATE یا DELETE استفاده شود.Forwarded from Mahi in Tech
توی سیستمهای High-Load، یکی از چالشهای همیشگی اینه که خیلی سریع بفهمیم یک دیتای خاص وجود داره یا نه. اگر بخوایم برای هر چک کردن ساده بهطور مستقیم سراغ دیتابیس بریم یا حتی به صورت کامل روی Cache حساب کنیم، هم منابع زیادی درگیر میشه و هم Latency بالا میره.
یکی از رویکردهای بهینه و جذاب برای حل این مسئله، استفاده از Bloom Filter هست.
بلوم فیلتر یک Data Structure احتمالاتی 🥴 هست که با کمترین میزان مصرف مموری و سرعت خیرهکننده، بهمون میگه یک آیتم وجود داره یا نه.
سناریوی واقعی: انتخاب یوزرنیم در تلگرام
تلگرام صدها میلیون کاربر داره. وقتی شما موقع ثبتنام داری یوزرنیم تایپ میکنی، به ازای هر کاراکتری که میزنی باید چک بشه که این یوزرنیم آزاد هست یا نه. اگر تلگرام بخواد برای هر تایپ شما یک کوئری به دیتابیس اصلیش بزنه، دیتابیس در عرض چند ثانیه از حجم درخواستها نابود میشه!
راهحل چیه؟ تلگرام تمام یوزرنیمهای ثبتشده رو میده به یک Bloom Filter که توی رم قرار داره. وقتی شما یوزرنیم جدید رو تایپ میکنی، بلوم فیلتر در کسری از میلیثانیه چک میکنه. اگر بگه «این یوزرنیم بهطور قطع وجود نداره»، تلگرام همون لحظه تیک سبز رو بهت نشون میده و دیگه کاری به دیتابیس نداره (صرفهجویی عظیم در منابع). اما اگر بلوم فیلتر بگه «ممکن هست وجود داشته باشه»، تلگرام تازه اونجا میره از دیتابیس میپرسه که "مطمئنی این یوزرنیم پر شده؟" تا وضعیت دقیق رو بهت بگه. (که البته تلگرام چنین کاری نمیکنه و مثال بود=))
حالا این بلوم فیلتر چطور کار میکنه؟
پشت صحنه، Bloom Filter در واقع فقط یک آرایه طولانی از Bitهاست که اول کار همهشون صفر هستن. در کنارش، چند تا تابع Hash مستقل و سریع هم داریم.
وقتی میخوایم یک دیتای جدید رو به سیستم اضافه کنیم، این دیتا رو به توابع Hash میدیم. خروجی این توابع، ایندکسهایی از همون آرایه بیتهاست. بعد میریم اون ایندکسها رو برابر با ۱ قرار میدیم.
موقع جستجو دوباره همون دیتای ورودی رو هش میکنیم و ایندکسها رو چک میکنیم:
۱. اگر حتی یکی از اون بیتها صفر باشه، سیستم با قطعیت ۱۰۰٪ میگه این دیتا «بهطور قطع وجود نداره».
۲. اگر همه بیتها ۱ باشن، سیستم میگه این دیتا «احتمالا وجود داره».
چرا میگیم احتمالا؟ چون ممکنه اون بیتها قبلا بهخاطر هش شدنِ دیتای دیگهای ۱ شده باشن (همون پدیده Hash Collision). یعنی ما توی Bloom Filter خطای False Positive داریم، اما False Negative اصلا نداریم.
البته که این ساختار محدودیتهای خودش رو هم داره؛ بهطور مثال حذف کردن یک آیتم از بلوم فیلتر در پیادهسازیهای استانداردش یهجورایی غیرممکنه؛ چون با صفر کردن یک بیت، ممکن هست دیتای دیگهای که از همون بیت استفاده میکرده رو هم خراب کنیم.
در نهایت، در ازای پذیرش اون احتمال کمِ False Positive، سیستمی به دست میاد که میتونه وجود میلیونها رکورد رو فقط با چند مگابایت RAM در لایه Application چک کنه و زیرساخت دیتابیس شما رو از شر درخواستهای بیهوده نجات بده.
یکی از رویکردهای بهینه و جذاب برای حل این مسئله، استفاده از Bloom Filter هست.
بلوم فیلتر یک Data Structure احتمالاتی 🥴 هست که با کمترین میزان مصرف مموری و سرعت خیرهکننده، بهمون میگه یک آیتم وجود داره یا نه.
سناریوی واقعی: انتخاب یوزرنیم در تلگرام
تلگرام صدها میلیون کاربر داره. وقتی شما موقع ثبتنام داری یوزرنیم تایپ میکنی، به ازای هر کاراکتری که میزنی باید چک بشه که این یوزرنیم آزاد هست یا نه. اگر تلگرام بخواد برای هر تایپ شما یک کوئری به دیتابیس اصلیش بزنه، دیتابیس در عرض چند ثانیه از حجم درخواستها نابود میشه!
راهحل چیه؟ تلگرام تمام یوزرنیمهای ثبتشده رو میده به یک Bloom Filter که توی رم قرار داره. وقتی شما یوزرنیم جدید رو تایپ میکنی، بلوم فیلتر در کسری از میلیثانیه چک میکنه. اگر بگه «این یوزرنیم بهطور قطع وجود نداره»، تلگرام همون لحظه تیک سبز رو بهت نشون میده و دیگه کاری به دیتابیس نداره (صرفهجویی عظیم در منابع). اما اگر بلوم فیلتر بگه «ممکن هست وجود داشته باشه»، تلگرام تازه اونجا میره از دیتابیس میپرسه که "مطمئنی این یوزرنیم پر شده؟" تا وضعیت دقیق رو بهت بگه. (که البته تلگرام چنین کاری نمیکنه و مثال بود=))
حالا این بلوم فیلتر چطور کار میکنه؟
پشت صحنه، Bloom Filter در واقع فقط یک آرایه طولانی از Bitهاست که اول کار همهشون صفر هستن. در کنارش، چند تا تابع Hash مستقل و سریع هم داریم.
وقتی میخوایم یک دیتای جدید رو به سیستم اضافه کنیم، این دیتا رو به توابع Hash میدیم. خروجی این توابع، ایندکسهایی از همون آرایه بیتهاست. بعد میریم اون ایندکسها رو برابر با ۱ قرار میدیم.
موقع جستجو دوباره همون دیتای ورودی رو هش میکنیم و ایندکسها رو چک میکنیم:
۱. اگر حتی یکی از اون بیتها صفر باشه، سیستم با قطعیت ۱۰۰٪ میگه این دیتا «بهطور قطع وجود نداره».
۲. اگر همه بیتها ۱ باشن، سیستم میگه این دیتا «احتمالا وجود داره».
چرا میگیم احتمالا؟ چون ممکنه اون بیتها قبلا بهخاطر هش شدنِ دیتای دیگهای ۱ شده باشن (همون پدیده Hash Collision). یعنی ما توی Bloom Filter خطای False Positive داریم، اما False Negative اصلا نداریم.
البته که این ساختار محدودیتهای خودش رو هم داره؛ بهطور مثال حذف کردن یک آیتم از بلوم فیلتر در پیادهسازیهای استانداردش یهجورایی غیرممکنه؛ چون با صفر کردن یک بیت، ممکن هست دیتای دیگهای که از همون بیت استفاده میکرده رو هم خراب کنیم.
در نهایت، در ازای پذیرش اون احتمال کمِ False Positive، سیستمی به دست میاد که میتونه وجود میلیونها رکورد رو فقط با چند مگابایت RAM در لایه Application چک کنه و زیرساخت دیتابیس شما رو از شر درخواستهای بیهوده نجات بده.
📦 چطور یک فایل چندگیگابایتی را Upload میکنیم و اگر اینترنت قطع شد، از همانجا ادامه میدهیم؟فرض کن داری یک فایل 8GB آپلود میکنی.
۵۰٪ آپلود شده...
Upload: 4.0 GB / 8.0 GB
بعد اینترنت قطع میشود. 😐
اگر Upload بهصورت یک درخواست ساده انجام شده باشد، ممکن است مجبور شوی دوباره از ابتدا شروع کنی.
یعنی:
8 GB
↓
Connection Lost
↓
💀
↓
Upload again
اما سیستمهای Resumable Upload دقیقاً برای حل همین مشکل طراحی شدهاند.
ایدهی اصلی خیلی ساده است:
فایل بزرگ را طوری منتقل کن که بتوانیم بدانیم تا کجای آن با موفقیت دریافت شده و بعداً از همان نقطه ادامه دهیم.
📌 بهجای یک Upload بزرگ، انتقال را قابل ادامه میکنیم
در یک Upload معمولی ممکن است کل فایل در یک درخواست ارسال شود:
Client
|
|------ 8GB ------> Server
اگر ارتباط وسط انتقال قطع شود، درخواست شکست میخورد و ادامهدادن آن دشوار است.
یک مثال واقعی از پروتکل tus
فرض کنیم Upload تا byte شمارهی
70 پیش رفته است. Client وضعیت Upload را میپرسد:HEAD /files/abc123
Server پاسخ میدهد:
Upload-Offset: 70
یعنی:
تا byte 70
قبلاً دریافت شده.
حالا Client ادامهی فایل را میفرستد:
PATCH /files/abc123
Upload-Offset: 70
Content-Type: application/offset+octet-stream
[remaining bytes]
ءServer بعد از دریافت آن بخش پاسخ میدهد:
Upload-Offset: 100
یعنی Upload حالا تا byte شمارهی 100 پیش رفته است.
این رفتار در specification رسمی tus تعریف شده و صرفاً یک الگوی پیشنهادی نیست.
در Resumable Upload، انتقال در چند درخواست انجام میشود:
Client
|
|--- Part 1 ---> Server
|
|--- Part 2 ---> Server
|
|--- Part 3 ---> Server
|
|--- Part 4 ---> Server
|
...
در مستندات رسمی Google Cloud نیز Resumable Upload دقیقاً بهعنوان روشی برای ادامهدادن انتقال بعد از اختلال ارتباط معرفی شده است؛ هر درخواست میتواند بخشی از Object را منتقل کند.
📌ءPause هم در اصل یعنی چه؟
یک نکتهی مهم:
ءPause و Resume الزاماً دو قابلیت کاملاً جدا نیستند.
وقتی Upload قابل Resume باشد، Client میتواند ارسال داده را متوقف کند و بعداً با همان Upload Session ادامه دهد؛ به شرط اینکه Session هنوز معتبر باشد.
مثلاً:
10:00
Upload → 2GB
10:05
Pause ⏸️
10:30
Resume ▶️
Continue → 2GB
در Google Cloud Storage، یک Resumable Upload با یک session URI انجام میشود و همان session برای ادامهی انتقال استفاده میشود. مستندات Google میگوید این session میتواند تا یک هفته فعال بماند.
پس یک نکتهی مهم داریم:
ءResume به وجود یک state/session قابل ادامه وابسته است.
آیا همیشه باید فایل را به Chunkهای کوچک تقسیم کنیم؟
نه.
این یکی از جاهایی است که خیلی از توضیحات ساده، بیش از حد کلیگویی میکنند. Resumable Upload الزاماً به معنی این نیست که:
8GB
↓
8000 × 1MB
حتماً باید چنین کاری انجام دهیم.
ءGoogle Cloud صراحتاً اشاره میکند که در بعضی شرایط بهتر است انتقال در یک chunk بزرگ انجام شود و chunkهای کوچکتر هزینه و latency بیشتری ایجاد میکنند. Chunking زمانی میتواند مفید باشد که مثلاً محدودیت اندازهی درخواست وجود داشته باشد یا بخواهیم مقدار دادهای که در صورت شکست دوباره باید ارسال شود را محدود کنیم.
پس:
ءChunking یک تکنیک برای پیادهسازی و کنترل Upload است؛ Resumability مفهوم بزرگتری است.
ءIntegrity Check هم مهم است
فرض کن فایل 8GB است.
ما فقط نمیخواهیم بگوییم:
8GB received ✅
باید مطمئن شویم:
چیزی که دریافت کردهایم همان چیزی است که Client قصد ارسالش را داشته.
برای همین، سیستمهای Storage میتوانند از checksum / hash برای بررسی integrity استفاده کنند.
ءGoogle Cloud در مستندات Resumable Upload توصیه میکند برای Object نهایی integrity check انجام شود؛ از جمله استفاده از
Content-MD5 برای بررسی اینکه Object نهایی با فایل اصلی مطابقت دارد.این موضوع مخصوصاً برای فایلهای بزرگ اهمیت بیشتری پیدا میکند، چون انتقال آنها زمان بیشتری طول میکشد.
#کلیشه
«این متن با AI نوشته شده، پس ارزش خوندن نداره.»
از کجا معلوم؟
شاید نویسنده، حرف خوبی برای گفتن داشته باشه، ولی نویسنده خوبی نباشه.
شاید فقط ایده رو داده باشه به AI
تا بهتر بیانش کنه.
ناتوانی در نوشتن، لزوما ناتوانی در فکر کردن نیست.
اگه همون حرف رو یک نویسندهی ضعیف ولی یک مهندس قوی گفته باشه و AI فقط بهتر بیانش کرده باشه، چی؟
حالا سوال:
ما کیفیت فکر رو قضاوت میکنیم
یا کیفیت جملهبندی رو؟
«این متن با AI نوشته شده، پس ارزش خوندن نداره.»
از کجا معلوم؟
شاید نویسنده، حرف خوبی برای گفتن داشته باشه، ولی نویسنده خوبی نباشه.
شاید فقط ایده رو داده باشه به AI
تا بهتر بیانش کنه.
ناتوانی در نوشتن، لزوما ناتوانی در فکر کردن نیست.
اگه همون حرف رو یک نویسندهی ضعیف ولی یک مهندس قوی گفته باشه و AI فقط بهتر بیانش کرده باشه، چی؟
حالا سوال:
ما کیفیت فکر رو قضاوت میکنیم
یا کیفیت جملهبندی رو؟