🚀 چگونه بدون استفاده از کتابخانههای خارجی یک Cache با کارایی بالا بسازیم
چند روز پیش داشتم به یک تکه کد نگاه میکردم که بیش از حد لازم کار انجام میداد 🤯
مطمئنم شما هم میتونید نمونهای مشابه ازش رو در اپلیکیشنهای خودتون پیدا کنید.
شاید یک فراخوانی دیتابیس باشه که باید سریعتر باشه 🐌،
یا یک API خارجی که کمکم داره برای هر درخواست ازتون هزینههای سنگین میگیره 💸.
اولین واکنش من معمولاً اینه:
👉 «خب، فقط cacheش میکنم.»
در NET.، این معمولاً یعنی استفاده از IMemoryCache
یا اضافه کردن یک distributed cache مثل Redis 🧠.
اما تا حالا شده از خودتون بپرسید واقعاً داخل این کتابخانهها چه اتفاقی میافته؟ 🤔
چرا برای ذخیرهی یک مقدار ساده در حافظه به اینهمه پیچیدگی نیاز داریم؟
برای همین، یک بعدازظهر رو صرف این کردم که یک cache با کارایی بالا رو از صفر بسازم ⏳⚙️.
من توصیه نمیکنم برای استفاده در محیط production خودتون یک کتابخانهی cache بنویسید ❌
اما من با انجام دادن یاد میگیرم ✍️.
درک این الگوها (concurrency، race condition و locking)
همون چیزیه که یک «coder» 👨💻 رو از یک مهندس نرمافزار واقعی 🧑🔧 جدا میکنه.
🧭 نقطهی شروع
من روی یک handler ساده برای تبدیل واحد ارز کار میکردم 💱.
ما برای گرفتن نرخ تبدیل ارزها از یک API شخص ثالث استفاده میکنیم 🌐.
این API نرخ تبدیل فعلی یک کد ارز مشخص
(مثل EUR، GBP، JPY) رو نسبت به USD برمیگردونه.
این پیادهسازی اولیه است 👇:
public static class CurrencyConversion
{
public static async Task<IResult> Handle(
string currencyCode,
decimal amount,
CurrencyApiClient currencyClient)
{
// Validate currency code format (3 uppercase letters)
if (string.IsNullOrWhiteSpace(currencyCode)||
currencyCode.Length != 3 ||
!currencyCode.All(char.IsLetter))
{
return Results.BadRequest(
new { error = "Currency code must be a 3-letter uppercase code (e.g., EUR, GBP)" });
}
// Validate amount (must be positive)
if (amount < 0)
{
return Results.BadRequest(new { error = "Amount must be a positive number" });
}
var rate = await currencyClient.GetExchangeRateAsync(currencyCode);
if (rate == null)
{
return Results.NotFound(
new { error = $"Exchange rate for {currencyCode} not found or API error occurred" });
}
var convertedAmount = amount * rate.Value;
return Results.Ok(new ExchangeRateResponse(
Currency: currencyCode,
BaseCurrency: "USD",
Rate: rate.Value,
Amount: amount,
ConvertedAmount: convertedAmount
));
}
}
این کد در محیط توسعهی لوکال کاملاً خوب کار میکنه ✅
اما در محیط production، اگه ۱۰۰ نفر همزمان به این endpoint درخواست بزنن 😬،
شما دارید ۱۰۰ فراخوانی شبکهی کاملاً یکسان انجام میدید 📡📡📡.
ارائهدهندهی API از شما متنفر میشه 😡
(و احتمالاً rate limit هم میشید 🚫)
و latency سیستمتون بهشدت افزایش پیدا میکنه 📈.
حالا بیاید بدون استفاده از هیچ کتابخانهی خارجی،
یک cache بسازیم تا این مشکل رو حل کنیم 🛠🧠.
یادتون باشه، این کار فقط برای یادگیری انجام میشه 🎓.