برنامه نویسی

Idempotence: بهبود انعطاف پذیری سیستم شما – قسمت دوم

Summarize this content to 400 words in Persian Lang
در پست قبلی به مفاهیم اولیه تاب آوری و عدم توانایی پرداختیم. علاوه بر این، نشان دادم که چگونه این مفاهیم را به صورت عملی در یک پروژه شخصی به کار بردم.

اکنون، من برخی از محدودیت‌های مدل اولیه پیشنهادی را بررسی می‌کنم و یک جایگزین مناسب برای اجرای ناتوانی، که کارایی بیشتری دارد، ارائه خواهم کرد.

محدودیت های تولید کلید Idempotence در Front-end چیست؟

در مدل پیشنهادی قبلی، کلید idempotency توسط front-end تولید می‌شود، در SessionStorage مرورگر ذخیره می‌شود و بعداً به back-end ارسال می‌شود. اگرچه این روش کار می کند، اما بهترین راه برای حل مشکل نیست.

اولاً، تولید کلید توسط قسمت جلویی به کاربر سیستم اجازه می دهد آن را دستکاری کند، زیرا هنگام باز کردن برگه ذخیره سازی مرورگر به راحتی قابل دسترسی است.

ثانیا، و دقیقاً به نکته اول مربوط می شود، قسمت جلویی بیشتر در معرض دستکاری و حملات سایبری است. به‌طور پیش‌فرض، قوانین کسب‌وکار نباید در قسمت جلویی ایجاد شود. در صورت لزوم، آنها باید در قسمت پشتی تقویت شوند.

راه حل چه خواهد بود؟

اولین و ساده ترین راه حل ایجاد یک نقطه پایانی در باطن برای تولید کلید idempotence و ذخیره آن در حافظه است. در این مورد، می‌توانیم از رویکرد تولید یک UUID به عنوان کلید ناتوانی استفاده کنیم.

دوم، قوی تر، استفاده از یک لایه کش برای ذخیره کلید idempotence برای یک زمان از پیش تعیین شده است. در این مورد، کلید idempotency می تواند از هش اطلاعات حساب فرستنده، اطلاعات حساب گیرنده، مقدار و توضیحات (در صورت وجود) ایجاد شود. این کلید برای مثال به مدت 5 دقیقه (یا هر بازه زمانی دیگری بسته به اجرا) در Redis ذخیره می شود. اگر درخواست به دلیل ناپایداری تکرار شود، کلید از حافظه پنهان گرفته می شود و تراکنش پیدا می شود.

در پیشنهاد دوم، من انتخاب نکردم که برای هر تراکنش یک UUID ایجاد کنم. این به این دلیل است که، اگر با هر درخواست برای ایجاد یک تراکنش، یک UUID ایجاد شود، حتی اگر یک درخواست تکراری باشد، باز هم دو درخواست متفاوت خواهند بود که منجر به UUID های متفاوت می شود. به این ترتیب، کلید ناتوانی ما واقعاً ناتوان نخواهد بود.

چرا این راه حل بهتری است؟

اول، چون ما نیاز به فیلد مربوط به کلید idempotence در پایگاه داده را حذف می کنیم، از بارگذاری بیش از حد پایگاه داده اصلی با پرس و جوها برای بررسی اینکه آیا تراکنش قبلاً وجود دارد یا خیر، اجتناب می کنیم. Querying Redis بسیار سریعتر، کارآمدتر و کم هزینه تر است.

دوم، به این دلیل که ما هر گونه قوانین تجاری را از قسمت جلو حذف می کنیم، بنابراین از آسیب پذیری بیشتر سیستم خود در برابر حملات و دستکاری ناخواسته داده ها جلوگیری می کنیم.

چگونه این راه حل را پیاده سازی کنیم؟

ابتدا بیایید Redis را در پروژه NodeJS خود نصب کنیم:

pnpm add ioredis

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بیایید یک نمونه Redis ایجاد کنیم:

import Redis from ‘ioredis’;

const redis = new Redis();

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حال، بیایید هش را ایجاد کنیم که کلید ناتوانی ما خواهد بود:

import crypto from ‘node:crypto’;

async createTransaction(data, ctx) {

const { value, description } = data;

const dataToBeHashed = `${senderAccount._id}-${receiverAccount?._id}-${value}-${description}`

const idempotentKey =
crypto.createHash(‘sha256’).update(dataToBeHashed).digest(‘hex’)


}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس این کلید idempotency را در حافظه پنهان قرار می دهیم و وجود آن را بررسی می کنیم:

async createTransaction(data, ctx) {

const existingTransaction = await redis.set(idempotentKey, ‘transaction’, ‘EX’, 60 * 5, ‘GET’)

if (existingTransaction) {
return false
}

}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این یک نمایش ساده از نحوه اجرای این رویکرد با NodeJS بدون توجه به چارچوب مورد استفاده، خواه Express، NestJS، Koa یا Fastify بود.

این پیاده سازی هنوز در مخزن پروژه موجود نیست، اما قصد دارم در چند روز آینده روی آن کار کنم.

این سری از پست ها برای به اشتراک گذاشتن سفر من در توسعه این پروژه و تصمیماتی که بر اساس مطالعات و تفکراتم گرفته ام ایجاد شده است.

من آماده بازخورد و پیشنهادات جدید هستم. من قصد دارم در آینده ویژگی‌های جدیدی را پیاده‌سازی کنم، بنابراین می‌توانم در لینکدین خود درباره این موضوع بیشتر صحبت کنم.

در پست قبلی به مفاهیم اولیه تاب آوری و عدم توانایی پرداختیم. علاوه بر این، نشان دادم که چگونه این مفاهیم را به صورت عملی در یک پروژه شخصی به کار بردم.

اکنون، من برخی از محدودیت‌های مدل اولیه پیشنهادی را بررسی می‌کنم و یک جایگزین مناسب برای اجرای ناتوانی، که کارایی بیشتری دارد، ارائه خواهم کرد.

محدودیت های تولید کلید Idempotence در Front-end چیست؟

در مدل پیشنهادی قبلی، کلید idempotency توسط front-end تولید می‌شود، در SessionStorage مرورگر ذخیره می‌شود و بعداً به back-end ارسال می‌شود. اگرچه این روش کار می کند، اما بهترین راه برای حل مشکل نیست.

اولاً، تولید کلید توسط قسمت جلویی به کاربر سیستم اجازه می دهد آن را دستکاری کند، زیرا هنگام باز کردن برگه ذخیره سازی مرورگر به راحتی قابل دسترسی است.

ثانیا، و دقیقاً به نکته اول مربوط می شود، قسمت جلویی بیشتر در معرض دستکاری و حملات سایبری است. به‌طور پیش‌فرض، قوانین کسب‌وکار نباید در قسمت جلویی ایجاد شود. در صورت لزوم، آنها باید در قسمت پشتی تقویت شوند.

راه حل چه خواهد بود؟

اولین و ساده ترین راه حل ایجاد یک نقطه پایانی در باطن برای تولید کلید idempotence و ذخیره آن در حافظه است. در این مورد، می‌توانیم از رویکرد تولید یک UUID به عنوان کلید ناتوانی استفاده کنیم.

دوم، قوی تر، استفاده از یک لایه کش برای ذخیره کلید idempotence برای یک زمان از پیش تعیین شده است. در این مورد، کلید idempotency می تواند از هش اطلاعات حساب فرستنده، اطلاعات حساب گیرنده، مقدار و توضیحات (در صورت وجود) ایجاد شود. این کلید برای مثال به مدت 5 دقیقه (یا هر بازه زمانی دیگری بسته به اجرا) در Redis ذخیره می شود. اگر درخواست به دلیل ناپایداری تکرار شود، کلید از حافظه پنهان گرفته می شود و تراکنش پیدا می شود.

در پیشنهاد دوم، من انتخاب نکردم که برای هر تراکنش یک UUID ایجاد کنم. این به این دلیل است که، اگر با هر درخواست برای ایجاد یک تراکنش، یک UUID ایجاد شود، حتی اگر یک درخواست تکراری باشد، باز هم دو درخواست متفاوت خواهند بود که منجر به UUID های متفاوت می شود. به این ترتیب، کلید ناتوانی ما واقعاً ناتوان نخواهد بود.

چرا این راه حل بهتری است؟

اول، چون ما نیاز به فیلد مربوط به کلید idempotence در پایگاه داده را حذف می کنیم، از بارگذاری بیش از حد پایگاه داده اصلی با پرس و جوها برای بررسی اینکه آیا تراکنش قبلاً وجود دارد یا خیر، اجتناب می کنیم. Querying Redis بسیار سریعتر، کارآمدتر و کم هزینه تر است.

دوم، به این دلیل که ما هر گونه قوانین تجاری را از قسمت جلو حذف می کنیم، بنابراین از آسیب پذیری بیشتر سیستم خود در برابر حملات و دستکاری ناخواسته داده ها جلوگیری می کنیم.

چگونه این راه حل را پیاده سازی کنیم؟

ابتدا بیایید Redis را در پروژه NodeJS خود نصب کنیم:

pnpm add ioredis
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بیایید یک نمونه Redis ایجاد کنیم:

import Redis from 'ioredis';

const redis = new Redis();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حال، بیایید هش را ایجاد کنیم که کلید ناتوانی ما خواهد بود:

import crypto from 'node:crypto';

async createTransaction(data, ctx) {
  ...
  const { value, description } = data;

  const dataToBeHashed = `${senderAccount._id}-${receiverAccount?._id}-${value}-${description}`

  const idempotentKey = 
  crypto.createHash('sha256').update(dataToBeHashed).digest('hex')

  ...
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس این کلید idempotency را در حافظه پنهان قرار می دهیم و وجود آن را بررسی می کنیم:

async createTransaction(data, ctx) {
   ...

   const existingTransaction = await redis.set(idempotentKey, 'transaction', 'EX', 60 * 5, 'GET')

   if (existingTransaction) {
     return false
   }
   ...
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این یک نمایش ساده از نحوه اجرای این رویکرد با NodeJS بدون توجه به چارچوب مورد استفاده، خواه Express، NestJS، Koa یا Fastify بود.

این پیاده سازی هنوز در مخزن پروژه موجود نیست، اما قصد دارم در چند روز آینده روی آن کار کنم.

این سری از پست ها برای به اشتراک گذاشتن سفر من در توسعه این پروژه و تصمیماتی که بر اساس مطالعات و تفکراتم گرفته ام ایجاد شده است.

من آماده بازخورد و پیشنهادات جدید هستم. من قصد دارم در آینده ویژگی‌های جدیدی را پیاده‌سازی کنم، بنابراین می‌توانم در لینکدین خود درباره این موضوع بیشتر صحبت کنم.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا