حلول للمشاكل العملية بعد النشر في البنية التحتية لـ PaaS الخادمة (Serverless)
TuBrief 편집팀
2026년 7월 24일
0
Internet Technology원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.
커뮤니티의 다른 글
댓글 (0)
Log in to leave a comment
아직 작성된 글이 없습니다
원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.
Log in to leave a comment
아직 작성된 글이 없습니다
تبدو منصات PaaS الشاملة التي تسمح بنشر الواجهة الأمامية والخلفية بنقرات قليلة مثالية في البداية. لكن المشكلة الحقيقية تبدأ عندما يدخل المستخدمون الفعليون. الكود الذي كان يعمل بشكل ممتاز محليًا يبدأ في إخراج أخطاء انتهاء المهلة (Timeout) بمجرد رفعه إلى بيئة الإنتاج، ومجرد محاولة تغيير مخطط قاعدة البيانات (DB Schema) قد تؤدي إلى تعطل الخدمة بأكملها. علاوة على ذلك، عندما تجد نفسك محتجزًا لدى مزود الخدمة (Vendor Lock-in) دون القدرة على التصرف، تبدأ المتاعب الحقيقية.
لقد قمت بتلخيص العديد من أنماط البنية التحتية التي تتيح لك الاستمتاع بسبل الراحة التي تقدمها هذه المنصات مع التخلص تمامًا من العقبات المصاحبة لها.
تظل عملية Node.js المحلية تعمل باستمرار، لكن البيئة الخادمة تظهر لفترة وجيزة عند ورود الطلب ثم تختفي. إذا قمت بإدارة المتغيرات العامة أو اتصالات DB المنفردة (Singleton Connections) بشكل سيئ خلال لحظة البداية الباردة (Cold Start)، فإن مجمع الاتصالات (Connection Pool) سينفد سريعًا.
ليس من المنطقي التحقق من ذلك عن طريق النشر في خادم البيئة التجريبية (Staging Server) في كل مرة. إن إحاطة طبقة الشبكة نفسها محليًا (Mocking) وتشغيل المعالج (Handler) يُعد أسرع بكثير.
`typescript
// src/mocks/handlers.ts
import { http, HttpResponse } from 'msw';
export const handlers = [
http.get('/api/v1/user/profile', ({ request }) => {
const authHeader = request.headers.get('Authorization');
if (!authHeader) {
return new HttpResponse(null, { status: 401, statusText: 'Unauthorized' });
}
return HttpResponse.json({
id: 'usr_102938',
email: 'dev@example.com',
role: 'ADMIN',
createdAt: new Date().toISOString()
});
})
];
// src/mocks/node.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);
`
عملية تطبيق هذا النمط بسيطة للغاية.
setupServer..env.local تمامًا عن نقطة نهاية مجمع المعاملات (Transaction Pooler) في بيئة الإنتاج.يمكنك التحقق من سلوك واجهة الخلفية بنسبة 100% وبنفس الطريقة محليًا دون الحاجة للضغط على زر النشر. إنها بنية تتيح لك الحصول على تعليقات فورية بمجرد كتابة الكود بدلاً من تضيع الوقت في الاختبارات عن بُعد غير المستقرة.
عند استخدام قاعدة بيانات PostgreSQL خادمة مثل Supabase أو Neon، فإن تشغيل عبارة ALTER TABLE البسيطة ينطوي على خطورة. حيث يتم فرض قفل ACCESS EXCLUSIVE على الجدول المستهدف، مما يجعل الاستعلامات اللاحقة تدخل في حالة الانتظار واحدة تلو الأخرى، ويؤدي ذلك في النهاية إلى تعطل الخدمة بسبب انتهاء المهلة.
أولاً، يجب عليك وضع حد لمهلة جلسة الترحيل.
`sql
SET lock_timeout = '2000ms';
SET statement_timeout = '5000ms';
ALTER TABLE users ADD COLUMN bio VARCHAR(255);
`
لتغيير هيكل قاعدة البيانات الشغالة أثنلء التشغيل بأمان، يجب عليك استخدام طريقة توسيع الكود وقاعدة البيانات معًا ثم تقليم الأجزاء القديمة لاحقًا.
`typescript
import { db } from './db';
interface UpdateUserProfileInput {
userId: string;
fullName: string;
}
export async function updateUserProfile({ userId, fullName }: UpdateUserProfileInput) {
await db.transaction(async (tx) => {
await tx.user.update({
where: { id: userId },
data: {
full_name: fullName,
name: fullName
}
});
});
}
`
فيما يلي ثلاث خطوات للعمل بآمان.
lock_timeout لإنشاء آلية أمان توقف العملية فورًا عند حدوث تنافس على القفل.من خلال اتباع هذه الإجراءات، يمكنك استبدال المخطط دون تجاهل أي طلب من طلبات المستخدمين.
في البيئة الخادمة، إذا تركت console.log فقط، فستختلط سجلات العديد من المثالات معًا وتصبح البيئة فوضوية. للتمكن من تتبع مسار الطلب منذ دخوله وحتى خروجه، تُعد معرفات الطلبات الفريدة (Request ID) وتنسيق JSON المنسق أمرًا ضروريًا.
`typescript
import { Hono } from 'hono';
import { requestId } from 'hono/request-id';
import pino from 'pino';
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
formatters: {
level: (label) => ({ level: label.toUpperCase() })
},
base: { env: process.env.NODE_ENV }
});
const app = new Hono();
app.use('', requestId());
app.use('', async (c, next) => {
const reqId = c.var.requestId;
const startTime = performance.now();
c.set('logger', logger.child({ reqId }));
await next();
const durationMs = Math.round(performance.now() - startTime);
logger.info({
reqId,
method: c.req.method,
path: c.req.path,
status: c.res.status,
durationMs
}, 'Request processing finished');
});
`
إليك الإعدادات لتجنب صدمة التكاليف الإضافية عند تجاوز الحدود أثناء استخدام أدوات المراقبة الخارجية.
Server-Timing في ترويسة الاستجابة لإرسال نقاط الاختناق إلى نظام المراقبة.`typescript
import * as Sentry from '@sentry/node';
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 0.1,
beforeSend(event, hint) {
const error = hint.originalException;
if (error && error instanceof Error && error.message.includes('ECONNRESET')) {
return null;
}
return event;
}
});
`
حتى لو قمت فقط بتصفية سجلات 200 OK غير الذات معنى والحالات الاستثنائية ذات الطابع الضوضائي، يمكنك التحكم في النظام بشكل كافٍ ضمن حدود الطبقة المجانية لمنصة المراقبة.
إذا قمت بربط SDK الخاص بـ PaaS معينة مباشرة بمنطق العمل الخاص بك (Business Logic)، فستضطر إلى إعادة كتابة الكود بالكامل لاحقًا عند الانتقال إلى منصة أخرى. هذا هو السبب في استخدام نمط المحول الذي نظمه Martin Fowler لفصل نقطة التماس بين المنطق الأساسي للتطبيق و SDK السحابي الخارجي.
`typescript
export interface IStorageService {
uploadFile(path: string, fileBuffer: Buffer, mimeType: string): Promise<{ url: string }>;
deleteFile(path: string): Promise;
}
import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3';
export class S3StorageAdapter implements IStorageService {
private s3: S3Client;
private bucket: string;
constructor(region: string, bucket: string) {
this.s3 = new S3Client({ region });
this.bucket = bucket;
}
async uploadFile(path: string, fileBuffer: Buffer, mimeType: string): Promise<{ url: string }> {
await this.s3.send(new PutObjectCommand({
Bucket: this.bucket,
Key: path,
Body: fileBuffer,
ContentType: mimeType
}));
return { url: https://${this.bucket}.s3.amazonaws.com/${path} };
}
async deleteFile(path: string): Promise {
await this.s3.send(new DeleteObjectCommand({ Bucket: this.bucket, Key: path }));
}
}
`
قم بتمتجة النسخ الاحتياطي للبيانات أيضًا حتى تتمكن من الخروج في أي وقت.
`bash
#!/usr/bin/env bash
set -euo pipefail
TIMESTAMP=(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/tmp/db_backup_{TIMESTAMP}"
S3_BUCKET="s3://my-app-exit-backups/pg_dumps"
export PGPASSWORD="${DB_PASSWORD}"
mkdir -p "${BACKUP_DIR}"
pg_dump -h "{DB_PORT}" -U "{DB_NAME}"
-Fc --exclude-table-data='logs_*' > "${BACKUP_DIR}/full_schema_data.dump"
tar -czvf "{BACKUP_DIR}" .
aws s3 cp "{S3_BUCKET}/{R2_ENDPOINT_URL}"
rm -rf "{BACKUP_DIR}.tar.gz"
`
pg_dump لاستخراج المخطط والبيانات.crontab لنقل ملف النسخ الاحتياطي إلى Cloudflare R2 أو دلو S3 خارجي.إن تقليل مخاطر التبعية لمنصة معينة يجعل من السهل جدًا التعامل مع ارتفاع أسعار البنية التحتية أو عند حدوث أعطال في المنصة.