Q:1- System Design (scalability, microservices)
Answer:
🏗️ System Design কী?
System Design হলো এমন একটি প্রক্রিয়া যেখানে আপনি একটি বড় সফটওয়্যার সিস্টেমের আর্কিটেকচার, কম্পোনেন্ট, এবং ডেটা ফ্লো পরিকল্পনা করেন — যাতে সিস্টেমটি লক্ষ লক্ষ ইউজার হ্যান্ডেল করতে পারে, ক্র্যাশ না করে, এবং দ্রুত রেসপন্স দিতে পারে।
সহজ কথায়: আপনার অ্যাপ যখন ১০০ ইউজার থেকে ১০ মিলিয়ন ইউজারে যায় — তখন কী করবেন, সেটাই System Design।
📐 ১. Scalability (স্কেলেবিলিটি)
Scalability মানে হলো আপনার সিস্টেম বেশি লোড আসলে সেটা সামলাতে পারে কিনা।
দুই ধরনের Scaling আছে:
Vertical Scaling (Scale Up)
মানে আপনার সার্ভারের শক্তি বাড়ানো — বেশি RAM, বেশি CPU। কিন্তু এর একটা সীমা আছে এবং এটা ব্যয়বহুল।
Horizontal Scaling (Scale Out)
মানে একটা সার্ভারের বদলে অনেকগুলো সার্ভার ব্যবহার করা। Laravel-এ এটাই বেশি প্র্যাক্টিক্যাল।
[User] → [Load Balancer] → [Server 1]
→ [Server 2]
→ [Server 3]
Laravel-এ Horizontal Scaling এর জন্য যা করতে হবে:
১. Session Shared করুন — একাধিক সার্ভারে session share করতে Redis ব্যবহার করুন:
php
// .env
SESSION_DRIVER=redis
CACHE_DRIVER=redis
// config/session.php
'driver' => env('SESSION_DRIVER', 'redis'),
২. File Storage Centralize করুন — সব সার্ভার যেন একই জায়গা থেকে ফাইল পায়:
php
// config/filesystems.php
'default' => env('FILESYSTEM_DISK', 's3'),
'disks' => [
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
],
],
৩. Queue Centralize করুন — Jobs যেন যেকোনো সার্ভার প্রসেস করতে পারে:
php
// .env
QUEUE_CONNECTION=redis
// Job dispatch করুন
ProcessOrder::dispatch($order)->onQueue('orders');
// যেকোনো সার্ভারে worker চালান
php artisan queue:work redis --queue=orders
🔄 ২. Scalability Patterns
ক) Database Read/Write Separation
বেশিরভাগ অ্যাপে ৮০% Read, ২০% Write অপারেশন হয়। তাই Read আলাদা সার্ভারে পাঠানো স্মার্ট।
php
// config/database.php
'mysql' => [
'read' => [
'host' => [
env('DB_READ_HOST_1', '192.168.1.2'),
env('DB_READ_HOST_2', '192.168.1.3'),
],
],
'write' => [
'host' => [env('DB_WRITE_HOST', '192.168.1.1')],
],
'driver' => 'mysql',
'database' => env('DB_DATABASE', 'myapp'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
],
এখন Laravel নিজেই SELECT কে Read সার্ভারে এবং INSERT/UPDATE/DELETE কে Write সার্ভারে পাঠাবে।
খ) Caching Layer
php
// ডাটাবেজে বারবার না গিয়ে Cache থেকে নিন
public function getPopularProducts()
{
return Cache::remember('popular_products', 3600, function () {
return Product::where('is_popular', true)
->with('category')
->take(20)
->get();
});
}
গ) Queue দিয়ে Heavy Tasks আলাদা করুন
php
// ❌ ভুল — Request এর মধ্যেই Heavy কাজ করা
public function register(Request $request)
{
$user = User::create($request->all());
Mail::send(new WelcomeMail($user)); // এটা slow!
Notification::send($user, new SmsNotification()); // এটাও slow!
return response()->json(['success' => true]);
}
// ✅ সঠিক — Queue এ পাঠিয়ে দিন
public function register(Request $request)
{
$user = User::create($request->all());
SendWelcomeEmail::dispatch($user); // Background এ যাবে
SendWelcomeSms::dispatch($user); // Background এ যাবে
return response()->json(['success' => true]); // তৎক্ষণাৎ রেসপন্স
}
🧩 ৩. Microservices Architecture
Microservices মানে একটা বড় অ্যাপকে ছোট ছোট স্বাধীন সার্ভিসে ভাগ করা — যেগুলো আলাদাভাবে deploy ও scale করা যায়।
Monolith vs Microservices:
❌ Monolith (একটাই বড় অ্যাপ):
[User, Product, Order, Payment, Notification] → একটা Laravel App
✅ Microservices (আলাদা আলাদা সার্ভিস):
[User Service] → api.myapp.com/users
[Product Service] → api.myapp.com/products
[Order Service] → api.myapp.com/orders
[Payment Service] → api.myapp.com/payments
[Notification Service]→ api.myapp.com/notifications
Microservices কখন দরকার?
আপনার অ্যাপ যদি এরকম হয়:
- টিম বড় এবং আলাদা আলাদা টিম আলাদা ফিচারে কাজ করে
- কিছু সার্ভিস বেশি scale দরকার (যেমন Payment আলাদা scale)
- Different technology দরকার বিভিন্ন অংশে
Laravel-এ Microservices — Service Communication:
HTTP দিয়ে সার্ভিস কল করুন (Guzzle/Http Facade):
php
// Order Service থেকে Product Service কে কল করা
class OrderService
{
public function createOrder(array $data)
{
// Product Service এ চেক করুন stock আছে কিনা
$response = Http::withToken(config('services.product.token'))
->get(config('services.product.url') . '/api/products/' . $data['product_id']);
if (!$response->successful()) {
throw new ServiceUnavailableException('Product service unreachable');
}
$product = $response->json();
if ($product['stock'] < $data['quantity']) {
throw new InsufficientStockException('Not enough stock');
}
// Order তৈরি করুন
return Order::create([
'user_id' => $data['user_id'],
'product_id' => $data['product_id'],
'quantity' => $data['quantity'],
'total' => $product['price'] * $data['quantity'],
]);
}
}
Event-Driven Communication (RabbitMQ/Redis):
php
// Order Service — Event Publish করছে
class OrderController extends Controller
{
public function store(Request $request)
{
$order = Order::create($request->validated());
// Notification Service কে জানাও — সরাসরি কল না করে Event দিয়ে
event(new OrderCreated($order));
// অথবা Redis Pub/Sub দিয়ে
Redis::publish('order.created', json_encode([
'order_id' => $order->id,
'user_id' => $order->user_id,
'total' => $order->total,
]));
return response()->json($order, 201);
}
}
// Notification Service — Event Subscribe করছে
class OrderCreatedListener
{
public function handle(OrderCreated $event)
{
// Email পাঠাও
Mail::to($event->order->user->email)
->send(new OrderConfirmationMail($event->order));
}
}
🚦 ৪. API Gateway Pattern
Microservices এ সব সার্ভিস আলাদা থাকলে Client কে একটাই Entry Point দেওয়া হয় — এটাই API Gateway।
[Mobile App] ──┐
[Web App] ──┤──→ [API Gateway] ──→ [User Service]
[Third Party] ──┘ │ ──→ [Product Service]
│ ──→ [Order Service]
(Auth, Rate Limit,
Logging, Routing)
Laravel-এ Simple API Gateway বানানো:
php
// routes/api.php — Gateway হিসেবে কাজ করবে
Route::middleware(['auth:sanctum', 'throttle:100,1'])->group(function () {
// User Service এ forward করুন
Route::any('/users/{path?}', function (Request $request, $path = '') {
return Http::withToken($request->bearerToken())
->send($request->method(),
config('services.user.url') . '/' . $path,
['json' => $request->all()]);
})->where('path', '.*');
// Product Service এ forward করুন
Route::any('/products/{path?}', function (Request $request, $path = '') {
return Http::send($request->method(),
config('services.product.url') . '/' . $path,
['json' => $request->all()]);
})->where('path', '.*');
});
📊 ৫. Real-world System Design Example
ধরুন আপনাকে একটা E-commerce সিস্টেম ডিজাইন করতে বলা হলো যেখানে ১০ লক্ষ ইউজার থাকবে:
┌─────────────────────────────┐
│ CDN (CloudFront) │ ← Static files, Images
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Load Balancer (Nginx) │ ← Traffic distribute
└──────┬───────────┬───────────┘
│ │
┌────────────▼─┐ ┌────▼────────────┐
│ Laravel App │ │ Laravel App │ ← Multiple instances
│ (Server 1) │ │ (Server 2) │
└──────┬───────┘ └────┬─────────────┘
│ │
┌────────────▼─────────────────▼────────────┐
│ │
┌────▼────┐ ┌──────────┐ ┌───────────────┐ │
│ Redis │ │ MySQL │ │ Elasticsearch│ │
│ (Cache/ │ │ (Master) │ │ (Search) │ │
│ Queue/ │ │ │ │ └───────────────┘ │
│ Session)│ │ Replica │ │
└─────────┘ │ (Read) │ ┌───────────────┐ │
└──────────┘ │ S3 (Storage) │ │
└───────────────┘ │
└────────────────────────────────────────────┘
এই ডিজাইনের প্রতিটা অংশের কাজ:
CDN — Images, CSS, JS ক্যাশ করে রাখে, সার্ভারে চাপ কমায়।
Load Balancer — Request গুলো Server 1 ও Server 2 এ ভাগ করে দেয়।
Redis — Session, Cache এবং Queue — তিনটাই হ্যান্ডেল করে।
MySQL Master-Replica — Master Write করে, Replica Read করে।
Elasticsearch — Product search দ্রুত করে।
S3 — User uploaded files রাখে।
🎯 ৬. Scalability Best Practices — Laravel এ
php
// ১. N+1 Query সমস্যা দূর করুন
// ❌ ভুল — প্রতিটা order এর জন্য আলাদা query
$orders = Order::all();
foreach ($orders as $order) {
echo $order->user->name; // প্রতিবার DB hit!
}
// ✅ সঠিক — Eager Loading ব্যবহার করুন
$orders = Order::with(['user', 'products', 'payment'])->get();
// ২. Pagination ব্যবহার করুন — সব data একসাথে না নিয়ে
// ❌ ভুল
$products = Product::all(); // ১০ লক্ষ row!
// ✅ সঠিক
$products = Product::paginate(20);
// ৩. Chunk দিয়ে বড় ডেটা প্রসেস করুন
Product::chunk(1000, function ($products) {
foreach ($products as $product) {
// প্রতি ১০০০টা করে process হবে, memory overflow হবে না
ProcessProduct::dispatch($product);
}
});
// ৪. Database Index ব্যবহার করুন
Schema::table('orders', function (Blueprint $table) {
$table->index(['user_id', 'status']); // Composite Index
$table->index('created_at'); // Date filter এর জন্য
});
// ৫. Rate Limiting — অতিরিক্ত Request ঠেকান
Route::middleware('throttle:60,1')->group(function () {
Route::get('/products', [ProductController::class, 'index']);
});
🛡️ ৭. Fault Tolerance — সিস্টেম যেন ক্র্যাশ না করে
php
// Circuit Breaker Pattern — একটা সার্ভিস বন্ধ হলে পুরো সিস্টেম যেন না পড়ে
class PaymentService
{
public function processPayment(Order $order)
{
try {
$response = Http::timeout(5) // ৫ সেকেন্ডের বেশি অপেক্ষা না করা
->retry(3, 1000) // ৩ বার try, ১ সেকেন্ড পরপর
->post('https://payment-gateway.com/api/charge', [
'amount' => $order->total,
'currency' => 'BDT',
]);
if ($response->failed()) {
// Fallback — alternative payment method try করুন
return $this->processWithAlternativeGateway($order);
}
return $response->json();
} catch (ConnectionException $e) {
// Gateway down — Queue তে রাখুন পরে retry করার জন্য
RetryPayment::dispatch($order)->delay(now()->addMinutes(5));
Log::error('Payment gateway down', ['order_id' => $order->id]);
throw new PaymentGatewayException('Payment will be processed shortly');
}
}
}
📝 সংক্ষেপে মনে রাখুন
System Design এর মূল বিষয়গুলো হলো — Scalability মানে বেশি লোড সামলানোর ক্ষমতা (Horizontal > Vertical), Microservices মানে বড় সিস্টেমকে ছোট ছোট স্বাধীন সার্ভিসে ভাগ করা, Caching মানে ডাটাবেজে বারবার না গিয়ে দ্রুত ডেটা দেওয়া, Queue মানে ভারী কাজ Background এ করা, এবং Fault Tolerance মানে এক অংশ ভাঙলেও বাকি সিস্টেম চালু রাখা।
Q:2 - APIs (REST, GraphQL, gRPC)
Answer:
🌐 API কী?
API (Application Programming Interface) হলো দুটো সফটওয়্যারের মধ্যে কথা বলার নিয়ম। যেমন — আপনার Laravel অ্যাপ যখন Mobile App কে ডেটা দেয়, সেটা API এর মাধ্যমে হয়।
[Mobile App] ←──── API ────→ [Laravel Backend] ←──→ [Database]
[Web App] ←──── API ────→ [Laravel Backend]
[Third Party]←──── API ────→ [Laravel Backend]
তিনটা প্রধান API টাইপ আছে — REST, GraphQL, এবং gRPC। প্রতিটার আলাদা সুবিধা ও ব্যবহার আছে।
🔵 PART 1 — REST API
REST API কী?
REST (Representational State Transfer) হলো সবচেয়ে জনপ্রিয় API স্টাইল। এটা HTTP Methods ব্যবহার করে ডেটা আদান-প্রদান করে।
GET /api/products → সব product দেখাও
GET /api/products/5 → ৫ নম্বর product দেখাও
POST /api/products → নতুন product তৈরি করো
PUT /api/products/5 → ৫ নম্বর product সম্পূর্ণ আপডেট করো
PATCH /api/products/5 → ৫ নম্বর product আংশিক আপডেট করো
DELETE /api/products/5 → ৫ নম্বর product মুছে ফেলো
Laravel-এ Professional REST API বানানো
Step 1 — Route তৈরি করুন:
php
// routes/api.php
use App\Http\Controllers\Api\V1\ProductController;
// API Versioning — v1, v2 আলাদা রাখুন
Route::prefix('v1')->group(function () {
// Public routes — Auth লাগবে না
Route::get('/products', [ProductController::class, 'index']);
Route::get('/products/{id}', [ProductController::class, 'show']);
// Protected routes — Auth লাগবে
Route::middleware('auth:sanctum')->group(function () {
Route::post('/products', [ProductController::class, 'store']);
Route::put('/products/{id}', [ProductController::class, 'update']);
Route::delete('/products/{id}', [ProductController::class, 'destroy']);
});
});
Step 2 — Form Request দিয়ে Validation:
php
// app/Http/Requests/Api/StoreProductRequest.php
class StoreProductRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => 'required|string|max:255',
'price' => 'required|numeric|min:0',
'stock' => 'required|integer|min:0',
'category_id' => 'required|exists:categories,id',
'description' => 'nullable|string|max:1000',
];
}
public function messages(): array
{
return [
'name.required' => 'পণ্যের নাম দিতে হবে',
'price.required' => 'দাম দিতে হবে',
'category_id.exists' => 'ক্যাটাগরি সঠিক নয়',
];
}
}
Step 3 — API Resource দিয়ে Response Format করুন:
php
// app/Http/Resources/ProductResource.php
class ProductResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'price' => number_format($this->price, 2),
'price_bdt' => '৳ ' . number_format($this->price, 2),
'stock' => $this->stock,
'in_stock' => $this->stock > 0,
'category' => new CategoryResource($this->whenLoaded('category')),
'images' => ImageResource::collection($this->whenLoaded('images')),
'created_at' => $this->created_at->format('d M Y'),
// Condition — শুধু Admin দেখবে
'cost_price' => $this->when(
auth()->user()?->isAdmin(),
$this->cost_price
),
];
}
}
// Collection Resource
class ProductCollection extends ResourceCollection
{
public function toArray(Request $request): array
{
return [
'data' => $this->collection,
'meta' => [
'total_products' => $this->total(),
'current_page' => $this->currentPage(),
],
];
}
}
Step 4 — Controller লিখুন:
php
// app/Http/Controllers/Api/V1/ProductController.php
class ProductController extends Controller
{
public function index(Request $request)
{
$products = Product::query()
->with(['category', 'images'])
->when($request->search, fn($q) =>
$q->where('name', 'like', "%{$request->search}%")
)
->when($request->category_id, fn($q) =>
$q->where('category_id', $request->category_id)
)
->when($request->min_price, fn($q) =>
$q->where('price', '>=', $request->min_price)
)
->latest()
->paginate($request->per_page ?? 15);
return ProductResource::collection($products);
}
public function store(StoreProductRequest $request)
{
$product = Product::create($request->validated());
return (new ProductResource($product))
->response()
->setStatusCode(201); // 201 Created
}
public function show(int $id)
{
$product = Product::with(['category', 'images', 'reviews'])
->findOrFail($id);
return new ProductResource($product);
}
public function update(UpdateProductRequest $request, int $id)
{
$product = Product::findOrFail($id);
$product->update($request->validated());
return new ProductResource($product);
}
public function destroy(int $id)
{
$product = Product::findOrFail($id);
$product->delete();
return response()->json([
'message' => 'Product deleted successfully'
], 200);
}
}
Step 5 — Global Exception Handler (সুন্দর Error Response):
php
// app/Exceptions/Handler.php
public function register(): void
{
$this->renderable(function (Throwable $e, Request $request) {
if ($request->expectsJson()) {
// Validation Error
if ($e instanceof ValidationException) {
return response()->json([
'success' => false,
'message' => 'Validation failed',
'errors' => $e->errors(),
], 422);
}
// Not Found
if ($e instanceof ModelNotFoundException) {
return response()->json([
'success' => false,
'message' => 'Resource not found',
], 404);
}
// Unauthorized
if ($e instanceof AuthenticationException) {
return response()->json([
'success' => false,
'message' => 'Unauthenticated. Please login first.',
], 401);
}
// Server Error
return response()->json([
'success' => false,
'message' => 'Something went wrong',
'debug' => config('app.debug') ? $e->getMessage() : null,
], 500);
}
});
}
REST API Response Structure — Best Practice:
json
// ✅ Success Response
{
"success": true,
"message": "Products fetched successfully",
"data": {
"id": 1,
"name": "iPhone 15",
"price": "1,200.00"
},
"meta": {
"total": 150,
"per_page": 15,
"current_page": 1
}
}
// ✅ Error Response
{
"success": false,
"message": "Validation failed",
"errors": {
"name": ["পণ্যের নাম দিতে হবে"],
"price": ["দাম দিতে হবে"]
}
}
🟠 PART 2 — GraphQL API
GraphQL কী এবং কেন REST থেকে আলাদা?
REST এ সমস্যা হলো — Over-fetching (বেশি ডেটা আসে) এবং Under-fetching (কম ডেটা আসে, আবার call করতে হয়)।
❌ REST এর সমস্যা:
GET /api/users/1 → পুরো user object আসে (name, email, phone, address, ...)
কিন্তু আমার শুধু name দরকার ছিল!
GET /api/users/1 → user আসে, কিন্তু তার orders নেই
GET /api/users/1/orders → আবার আলাদা call করতে হলো!
✅ GraphQL এর সমাধান:
যা চাইবেন, ঠিক তাই পাবেন — এক request এ!
Laravel-এ GraphQL Setup (Lighthouse):
bash
composer require nuwave/lighthouse
php artisan vendor:publish --tag=lighthouse-schema
Schema Define করুন:
graphql
# graphql/schema.graphql
type Query {
# Single product
product(id: ID! @eq): Product @find
# Product list with filtering & pagination
products(
search: String @where(operator: "like", key: "name")
category_id: ID @eq
min_price: Float @where(operator: ">=", key: "price")
): [Product!]! @paginate(defaultCount: 15)
# Current logged in user
me: User @auth
}
type Mutation {
createProduct(input: CreateProductInput! @spread): Product
@create
@middleware(checks: ["auth:sanctum"])
updateProduct(id: ID!, input: UpdateProductInput! @spread): Product
@update
@middleware(checks: ["auth:sanctum"])
deleteProduct(id: ID!): Product
@delete
@middleware(checks: ["auth:sanctum"])
}
# Types
type Product {
id: ID!
name: String!
price: Float!
stock: Int!
in_stock: Boolean! @method(name: "isInStock")
category: Category @belongsTo
images: [Image!]! @hasMany
reviews: [Review!]! @hasMany
created_at: DateTime!
}
type User {
id: ID!
name: String!
email: String!
orders: [Order!]! @hasMany
}
type Category {
id: ID!
name: String!
products: [Product!]! @hasMany
}
# Input Types
input CreateProductInput {
name: String!
price: Float!
stock: Int!
category_id: ID!
description: String
}
input UpdateProductInput {
name: String
price: Float
stock: Int
}
GraphQL Query — Client থেকে এভাবে request করবেন:
graphql
শুধু দরকারি field নিন
query GetProduct {
product(id: 1) {
id
name
price
category {
name
}
}
}
ফলাফল — শুধু যা চেয়েছেন তাই আসবে:
{
"data": {
"product": {
"id": "1",
"name": "iPhone 15",
"price": 1200.00,
"category": {
"name": "Electronics"
}
}
}
}
একসাথে অনেক কিছু নিন — একটাই Request!
query GetDashboard {
me {
name
email
orders {
id
total
status
}
}
products(category_id: 1) {
data {
id
name
price
}
paginatorInfo {
total
currentPage
}
}
}
Mutation — Data তৈরি করুন
mutation CreateProduct {
createProduct(input: {
name: "Samsung Galaxy S24"
price: 999.99
stock: 50
category_id: 1
}) {
id
name
price
}
}
Custom Resolver — Complex Logic এর জন্য:
php
// app/GraphQL/Queries/ProductSearch.php
class ProductSearch
{
public function __invoke($_, array $args)
{
return Product::query()
->when(isset($args['search']), fn($q) =>
$q->where('name', 'like', "%{$args['search']}%")
->orWhere('description', 'like', "%{$args['search']}%")
)
->when(isset($args['min_price']), fn($q) =>
$q->where('price', '>=', $args['min_price'])
)
->with(['category', 'images'])
->get();
}
}
// app/GraphQL/Mutations/CreateOrder.php
class CreateOrder
{
public function __invoke($_, array $args)
{
// Stock চেক করুন
$product = Product::lockForUpdate()->findOrFail($args['product_id']);
if ($product->stock < $args['quantity']) {
throw new \Exception('Insufficient stock available');
}
// Transaction এ Order তৈরি করুন
return DB::transaction(function () use ($product, $args) {
$order = Order::create([
'user_id' => auth()->id(),
'product_id' => $product->id,
'quantity' => $args['quantity'],
'total' => $product->price * $args['quantity'],
]);
$product->decrement('stock', $args['quantity']);
return $order;
});
}
}
🟣 PART 3 — gRPC API
gRPC কী?
gRPC হলো Google এর তৈরি অত্যন্ত দ্রুত API প্রোটোকল। এটা REST এর চেয়ে ৫-১০ গুণ দ্রুত কাজ করে। Microservices এর মধ্যে communication এর জন্য এটা সেরা।
REST → JSON (Text) → HTTP/1.1 → ধীর, বড় size
gRPC → Protobuf (Binary) → HTTP/2 → দ্রুত, ছোট size
কখন gRPC ব্যবহার করবেন?
যখন দুটো internal service এর মধ্যে খুব দ্রুত এবং অনেক বেশি communication দরকার — যেমন Order Service থেকে Inventory Service কে প্রতি সেকেন্ডে হাজারবার call করতে হচ্ছে।
Laravel-এ gRPC Setup:
bash
# Protocol Buffer compiler install করুন
pecl install grpc
pecl install protobuf
composer require grpc/grpc
Proto File — API Contract Define করুন:
protobuf
// proto/product.proto
syntax = "proto3";
package product;
// Service Define
service ProductService {
rpc GetProduct (ProductRequest) returns (ProductResponse);
rpc GetProducts (ProductsRequest) returns (ProductsResponse);
rpc CreateProduct (CreateProductRequest) returns (ProductResponse);
rpc CheckStock (StockRequest) returns (StockResponse);
}
// Messages (REST এ JSON এর মতো)
message ProductRequest {
int32 id = 1;
}
message ProductResponse {
int32 id = 1;
string name = 2;
double price = 3;
int32 stock = 4;
bool in_stock = 5;
}
message StockRequest {
int32 product_id = 1;
int32 quantity = 2;
}
message StockResponse {
bool available = 1;
int32 current_stock = 2;
string message = 3;
}
message CreateProductRequest {
string name = 1;
double price = 2;
int32 stock = 3;
int32 category_id = 4;
}
gRPC Server (Product Service):
php
// app/Grpc/ProductServiceServer.php
class ProductServiceServer extends ProductServiceInterface
{
// Product খুঁজে দিন
public function GetProduct(ProductRequest $request, array $metadata = []): array
{
$product = Product::find($request->getId());
if (!$product) {
return [
new ProductResponse(),
Status::status(Code::NOT_FOUND, 'Product not found')
];
}
$response = new ProductResponse();
$response->setId($product->id);
$response->setName($product->name);
$response->setPrice($product->price);
$response->setStock($product->stock);
$response->setInStock($product->stock > 0);
return [$response, Status::ok()];
}
// Stock চেক করুন
public function CheckStock(StockRequest $request, array $metadata = []): array
{
$product = Product::find($request->getProductId());
$response = new StockResponse();
if (!$product) {
$response->setAvailable(false);
$response->setCurrentStock(0);
$response->setMessage('Product not found');
} elseif ($product->stock >= $request->getQuantity()) {
$response->setAvailable(true);
$response->setCurrentStock($product->stock);
$response->setMessage('Stock available');
} else {
$response->setAvailable(false);
$response->setCurrentStock($product->stock);
$response->setMessage("Only {$product->stock} items available");
}
return [$response, Status::ok()];
}
}
gRPC Client (Order Service থেকে Product Service কে call করা):
php
// app/Services/ProductGrpcClient.php
class ProductGrpcClient
{
private ProductServiceClient $client;
public function __construct()
{
// Product Service এর address
$this->client = new ProductServiceClient(
'product-service:50051', // Internal DNS
['credentials' => ChannelCredentials::createInsecure()]
);
}
public function checkStock(int $productId, int $quantity): array
{
$request = new StockRequest();
$request->setProductId($productId);
$request->setQuantity($quantity);
[$response, $status] = $this->client->CheckStock($request)->wait();
if ($status->code !== Code::OK) {
throw new \Exception('gRPC Error: ' . $status->details);
}
return [
'available' => $response->getAvailable(),
'current_stock' => $response->getCurrentStock(),
'message' => $response->getMessage(),
];
}
}
// Order Service এ ব্যবহার করুন
class OrderController extends Controller
{
public function store(Request $request, ProductGrpcClient $productClient)
{
// gRPC দিয়ে Product Service কে call — অত্যন্ত দ্রুত!
$stockInfo = $productClient->checkStock(
$request->product_id,
$request->quantity
);
if (!$stockInfo['available']) {
return response()->json([
'message' => $stockInfo['message']
], 422);
}
$order = Order::create($request->validated());
return response()->json($order, 201);
}
}
⚖️ REST vs GraphQL vs gRPC — কোনটা কখন?
┌─────────────┬──────────────┬──────────────┬──────────────┐
│ │ REST │ GraphQL │ gRPC │
├─────────────┼──────────────┼──────────────┼──────────────┤
│ সেরা ব্যবহার│ Public API │ Complex UI │ Microservice │
│ │ │ Mobile App │ Internal API │
├─────────────┼──────────────┼──────────────┼──────────────┤
│ Speed │ ⭐⭐⭐ │ ⭐⭐⭐ │ ⭐⭐⭐⭐⭐ │
├─────────────┼──────────────┼──────────────┼──────────────┤
│ শেখা সহজ │ ✅ সহজ │ মাঝামাঝি │ ❌ কঠিন │
├─────────────┼──────────────┼──────────────┼──────────────┤
│ Flexibility │ কম │ ✅ অনেক বেশি│ কম │
├─────────────┼──────────────┼──────────────┼──────────────┤
│ Browser │ ✅ সরাসরি │ ✅ সরাসরি │ ❌ কঠিন │
│ Support │ │ │ │
└─────────────┴──────────────┴──────────────┴──────────────┘
Real Project এ কীভাবে ব্যবহার করবেন:
আপনার E-commerce App:
REST API → Third-party integration, Payment Gateway, Public API
GraphQL → Mobile App, Web Dashboard (যেখানে flexible data দরকার)
gRPC → Order Service ↔ Inventory Service (Internal fast communication)
🔐 API Security — সব API তে দরকার
php
// ১. Rate Limiting — প্রতি মিনিটে সর্বোচ্চ কতবার call করা যাবে
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by(
$request->user()?->id ?: $request->ip()
);
});
// ২. API Versioning — পুরনো clients ভাঙবে না
Route::prefix('v1')->group(...); // পুরনো version চলবে
Route::prefix('v2')->group(...); // নতুন version আলাদা
// ৩. Request Logging — কে কখন কী করলো
class ApiLoggingMiddleware
{
public function handle(Request $request, Closure $next)
{
$response = $next($request);
Log::channel('api')->info('API Request', [
'method' => $request->method(),
'url' => $request->fullUrl(),
'user_id' => auth()->id(),
'ip' => $request->ip(),
'status' => $response->getStatusCode(),
'duration_ms' => round((microtime(true) - LARAVEL_START) * 1000),
]);
return $response;
}
}
// ৪. Response Cache — একই request বারবার DB তে না গিয়ে
Route::middleware('cache.headers:public;max_age=3600;etag')->group(function () {
Route::get('/products', [ProductController::class, 'index']);
});
📝 সংক্ষেপে মনে রাখুন
REST হলো সবচেয়ে সহজ ও জনপ্রিয় — Public API এর জন্য সেরা। GraphQL হলো Flexible — Mobile/Web App এ যখন কতটুকু ডেটা দরকার সেটা Client ঠিক করে। gRPC হলো সবচেয়ে দ্রুত — Microservices এর internal communication এ সেরা।
Laravel Backend Engineer হিসেবে REST অবশ্যই আয়ত্ত করুন, তারপর GraphQL শিখুন, এবং যখন Microservices নিয়ে কাজ করবেন তখন gRPC শিখুন।
Q:3 - Database Systems (SQL, NoSQL) ?
ANSWER:
🗄️ Database কী?
Database হলো ডেটা সংরক্ষণের সিস্টেম। আপনার অ্যাপের সব ডেটা — User, Product, Order — সব কিছু Database এ থাকে।
দুই ধরনের Database আছে:
SQL (Relational) NoSQL (Non-Relational)
───────────────── ──────────────────────
MySQL, PostgreSQL vs MongoDB, Redis
টেবিল আকারে ডেটা Document/Key-Value আকারে
Fixed Schema Flexible Schema
Relations আছে Relations নেই (বেশিরভাগ)
🔵 PART 1 — SQL Database
SQL কী?
SQL Database এ ডেটা টেবিল আকারে থাকে — অনেকটা Excel Spreadsheet এর মতো। প্রতিটা Row হলো একটা Record, প্রতিটা Column হলো একটা Field।
products টেবিল:
┌────┬─────────────┬────────┬───────┬─────────────┐
│ id │ name │ price │ stock │ category_id │
├────┼─────────────┼────────┼───────┼─────────────┤
│ 1 │ iPhone 15 │ 1200 │ 50 │ 1 │
│ 2 │ Samsung S24 │ 999 │ 30 │ 1 │
│ 3 │ MacBook Pro │ 2500 │ 20 │ 2 │
└────┴─────────────┴────────┴───────┴─────────────┘
Laravel-এ Database Migration — Professional ভাবে:
php
// database/migrations/create_products_table.php
Schema::create('products', function (Blueprint $table) {
// Primary Key
$table->id(); // Auto increment bigint
// Basic Fields
$table->string('name', 255);
$table->string('slug')->unique(); // URL friendly name
$table->text('description')->nullable();
$table->decimal('price', 10, 2); // 99999999.99 পর্যন্ত
$table->decimal('cost_price', 10, 2)->default(0);
$table->unsignedInteger('stock')->default(0);
$table->unsignedInteger('min_stock')->default(5); // Low stock alert
// Status
$table->enum('status', ['active', 'inactive', 'draft'])->default('active');
$table->boolean('is_featured')->default(false);
// Foreign Key — Category এর সাথে সম্পর্ক
$table->foreignId('category_id')
->constrained('categories')
->onDelete('restrict'); // Category মুছলে Error দেবে
$table->foreignId('brand_id')
->nullable()
->constrained('brands')
->onDelete('set null'); // Brand মুছলে NULL হবে
// Performance এর জন্য Index
$table->index('status');
$table->index('category_id');
$table->index(['price', 'status']); // Composite Index
$table->fullText(['name', 'description']); // Full-text search
// Timestamps
$table->timestamps(); // created_at, updated_at
$table->softDeletes(); // deleted_at — পুরোপুরি মুছবে না
});
Eloquent Relationships — টেবিলের মধ্যে সম্পর্ক:
php
// ১. One to Many — একটা Category তে অনেক Product
// Category Model
class Category extends Model
{
public function products(): HasMany
{
return $this->hasMany(Product::class);
}
// শুধু Active Product
public function activeProducts(): HasMany
{
return $this->hasMany(Product::class)
->where('status', 'active');
}
}
// ২. Many to Many — একটা Product এ অনেক Tag,
// একটা Tag এ অনেক Product
class Product extends Model
{
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class, 'product_tags')
->withPivot('added_by') // Pivot table এর extra field
->withTimestamps();
}
}
// ৩. Has Many Through — User এর Orders এর Products
class User extends Model
{
public function purchasedProducts(): HasManyThrough
{
return $this->hasManyThrough(
Product::class,
Order::class,
'user_id', // orders.user_id
'id', // products.id
'id', // users.id
'product_id' // orders.product_id
);
}
}
// ৪. Polymorphic — Comment একসাথে Product ও Blog এ থাকতে পারে
class Comment extends Model
{
public function commentable(): MorphTo
{
return $this->morphTo();
}
}
class Product extends Model
{
public function comments(): MorphMany
{
return $this->morphMany(Comment::class, 'commentable');
}
}
Advanced Eloquent Queries:
php
// ১. Complex Query Builder
$products = Product::query()
->select([
'products.*',
DB::raw('(SELECT AVG(rating) FROM reviews
WHERE reviews.product_id = products.id) as avg_rating'),
DB::raw('(SELECT COUNT(*) FROM order_items
WHERE order_items.product_id = products.id) as total_sold'),
])
->with(['category:id,name', 'brand:id,name', 'images'])
->withCount('reviews')
->where('status', 'active')
->where('stock', '>', 0)
->whereBetween('price', [500, 2000])
->whereHas('category', fn($q) => $q->where('is_active', true))
->when(request('search'), fn($q, $search) =>
$q->where(function($query) use ($search) {
$query->where('name', 'like', "%{$search}%")
->orWhere('description', 'like', "%{$search}%");
})
)
->orderByDesc('total_sold')
->orderByDesc('avg_rating')
->paginate(20);
// ২. Subquery — সবচেয়ে বেশি বিক্রি হওয়া Product
$topProducts = Product::addSelect([
'last_order_date' => Order::select('created_at')
->whereColumn('product_id', 'products.id')
->latest()
->limit(1),
])->get();
// ৩. Aggregation — Sales Report
$salesReport = Order::query()
->select([
DB::raw('DATE(created_at) as date'),
DB::raw('COUNT(*) as total_orders'),
DB::raw('SUM(total) as revenue'),
DB::raw('AVG(total) as avg_order_value'),
DB::raw('MAX(total) as highest_order'),
])
->where('status', 'completed')
->whereBetween('created_at', [
now()->startOfMonth(),
now()->endOfMonth()
])
->groupBy('date')
->orderBy('date')
->get();
// ৪. Transaction — একসাথে অনেক কাজ, একটা ব্যর্থ হলে সব Rollback
DB::transaction(function () use ($request) {
// Order তৈরি করুন
$order = Order::create([
'user_id' => auth()->id(),
'total' => $request->total,
'status' => 'pending',
]);
// প্রতিটা Product এর জন্য OrderItem তৈরি করুন
foreach ($request->items as $item) {
// Stock check with lock — Race condition এড়াতে
$product = Product::lockForUpdate()->findOrFail($item['product_id']);
if ($product->stock < $item['quantity']) {
throw new \Exception("{$product->name} এর stock নেই");
}
OrderItem::create([
'order_id' => $order->id,
'product_id' => $product->id,
'quantity' => $item['quantity'],
'price' => $product->price,
]);
// Stock কমিয়ে দিন
$product->decrement('stock', $item['quantity']);
}
// Payment record তৈরি করুন
Payment::create([
'order_id' => $order->id,
'amount' => $order->total,
'method' => $request->payment_method,
]);
return $order;
});
Database Performance — SQL Optimization:
php
// ১. N+1 Query সমস্যা — সবচেয়ে Common ভুল
// ❌ ভুল — ১০০ Product এর জন্য ১০১ টা Query!
$products = Product::all();
foreach ($products as $product) {
echo $product->category->name; // প্রতিবার DB hit
echo $product->brand->name; // আবার DB hit
}
// ✅ সঠিক — মাত্র ৩টা Query
$products = Product::with(['category', 'brand'])->get();
// ২. Lazy Loading vs Eager Loading
// Lazy — শুধু দরকার হলে load হবে (সাবধানে ব্যবহার করুন)
$product = Product::find(1);
if ($showCategory) {
$product->load('category'); // এখন load হবে
}
// ৩. Chunking — লক্ষ লক্ষ Record Process করুন Memory Overflow ছাড়া
Product::with('category')
->where('status', 'active')
->chunkById(500, function ($products) {
foreach ($products as $product) {
// প্রতি ৫০০টা করে process হবে
GenerateProductReport::dispatch($product);
}
});
// ৪. Cursor — আরো কম Memory তে
foreach (Product::cursor() as $product) {
// একটা একটা করে process — memory খুব কম লাগে
$product->update(['slug' => Str::slug($product->name)]);
}
// ৫. Index ব্যবহার করুন — Query দ্রুত হবে
// Migration এ:
$table->index('status'); // Single index
$table->index(['category_id', 'status']); // Composite index
$table->index('created_at'); // Date filter এর জন্য
// ৬. Query Cache
$popularProducts = Cache::remember('popular_products', 3600, function () {
return Product::with('category')
->where('is_featured', true)
->orderByDesc('total_sold')
->take(10)
->get();
});
// Cache clear করুন Product update হলে
Product::observe(ProductObserver::class);
class ProductObserver
{
public function saved(Product $product): void
{
Cache::forget('popular_products');
Cache::forget("product_{$product->id}");
}
}
PostgreSQL — MySQL এর চেয়ে শক্তিশালী কোথায়?
php
// ১. JSON Column — Flexible Data Store করুন
Schema::table('products', function (Blueprint $table) {
$table->jsonb('attributes')->nullable(); // jsonb — indexed & faster
$table->jsonb('metadata')->nullable();
});
// JSON এ data store করুন
$product->update([
'attributes' => [
'color' => 'Space Black',
'storage' => '256GB',
'ram' => '8GB',
]
]);
// JSON field দিয়ে query করুন
$blackPhones = Product::where('attributes->color', 'Space Black')
->where('attributes->storage', '256GB')
->get();
// ২. Full-Text Search — PostgreSQL অনেক ভালো
$products = Product::whereRaw(
"to_tsvector('english', name || ' ' || description)
@@ plainto_tsquery('english', ?)",
[$request->search]
)->get();
// ৩. Window Functions — Advanced Analytics
$rankedProducts = DB::select("
SELECT
id, name, price, category_id,
RANK() OVER (
PARTITION BY category_id
ORDER BY price DESC
) as price_rank,
SUM(price) OVER (
PARTITION BY category_id
) as category_total
FROM products
WHERE status = 'active'
");
🟠 PART 2 — NoSQL Database
NoSQL কী এবং কেন দরকার?
NoSQL এ ডেটা Document আকারে থাকে — অনেকটা JSON এর মতো। Fixed Schema নেই, যেকোনো Structure এ ডেটা রাখা যায়।
SQL (MySQL): NoSQL (MongoDB):
────────────────── ────────────────────────────
টেবিল → Row → Column Collection → Document → Field
products টেবিল: products collection:
id | name | price {
1 | iPhone | 1200 "_id": "abc123",
"name": "iPhone 15",
"price": 1200,
"variants": [ ← Nested!
{"color": "Black", "stock": 20},
{"color": "White", "stock": 15}
],
"specs": { ← Flexible!
"ram": "8GB",
"storage": "256GB"
}
}
MongoDB — Laravel Setup:
bashcomposer require mongodb/laravel-mongodb
php
// config/database.php
'mongodb' => [
'driver' => 'mongodb',
'host' => env('MONGODB_HOST', '127.0.0.1'),
'port' => env('MONGODB_PORT', 27017),
'database' => env('MONGODB_DATABASE', 'myapp'),
'username' => env('MONGODB_USERNAME', ''),
'password' => env('MONGODB_PASSWORD', ''),
],
// Model
class Product extends Model
{
protected $connection = 'mongodb';
protected $collection = 'products';
protected $fillable = [
'name', 'price', 'stock', 'attributes',
'variants', 'images', 'category'
];
}
MongoDB CRUD Operations:
php
// ১. Create — Flexible Structure
$product = Product::create([
'name' => 'iPhone 15 Pro',
'price' => 1299,
'stock' => 100,
// Nested Object — SQL এ আলাদা টেবিল লাগতো
'category' => [
'id' => 1,
'name' => 'Smartphones',
'slug' => 'smartphones',
],
// Array of Objects — SQL এ Junction Table লাগতো
'variants' => [
['color' => 'Natural Titanium', 'storage' => '128GB', 'stock' => 30, 'price' => 1299],
['color' => 'Black Titanium', 'storage' => '256GB', 'stock' => 25, 'price' => 1499],
['color' => 'White Titanium', 'storage' => '512GB', 'stock' => 15, 'price' => 1699],
],
// Completely Different Attributes per Product
'specs' => [
'chip' => 'A17 Pro',
'display' => '6.1 inch Super Retina XDR',
'camera' => '48MP Main + 12MP Ultra Wide',
'battery' => '3274 mAh',
],
'tags' => ['flagship', 'apple', '5g', 'pro'],
'images' => [
['url' => 'front.jpg', 'is_primary' => true],
['url' => 'back.jpg', 'is_primary' => false],
],
]);
// ২. Read — Nested Field Query
// Nested field এ search
$blackPhones = Product::where('variants.color', 'Black Titanium')->get();
// Array এ value খুঁজুন
$taggedProducts = Product::where('tags', 'flagship')->get();
// Nested object এ range query
$affordableVariants = Product::where('variants.price', '<=', 1500)->get();
// ৩. Update — Nested Array Update
// Array তে নতুন item যোগ করুন
$product->push('tags', 'bestseller');
// Nested object update করুন
$product->update(['specs.battery' => '3500 mAh']);
// Array থেকে item সরান
$product->pull('tags', 'old-tag');
// ৪. Aggregation Pipeline — Complex Analytics
$categoryStats = Product::raw(function($collection) {
return $collection->aggregate([
// Stage 1: Filter
['$match' => ['status' => 'active']],
// Stage 2: Unwind array
['$unwind' => '$variants'],
// Stage 3: Group করুন
['$group' => [
'_id' => '$category.name',
'total_products' => ['$sum' => 1],
'avg_price' => ['$avg' => '$variants.price'],
'total_stock' => ['$sum' => '$variants.stock'],
'min_price' => ['$min' => '$variants.price'],
'max_price' => ['$max' => '$variants.price'],
]],
// Stage 4: Sort
['$sort' => ['total_products' => -1]],
// Stage 5: Limit
['$limit' => 10],
]);
});
Redis — সবচেয়ে দ্রুত NoSQL:
Redis হলো In-Memory Database — সব ডেটা RAM এ থাকে, তাই অত্যন্ত দ্রুত। এটা মূলত Cache, Session, Queue, এবং Real-time কাজে ব্যবহার হয়।
php
// ১. Simple Cache
// Set — ১ ঘন্টার জন্য cache করুন
Redis::setex('product:1', 3600, json_encode($product));
// Get
$product = json_decode(Redis::get('product:1'));
// Delete
Redis::del('product:1');
// ২. Hash — Object Store করুন
Redis::hmset("user:{$userId}", [
'name' => 'রাকিব হাসান',
'email' => 'rakib@example.com',
'last_login' => now()->toDateTimeString(),
'total_orders' => 25,
]);
// একটা field পড়ুন
$name = Redis::hget("user:{$userId}", 'name');
// সব field পড়ুন
$user = Redis::hgetall("user:{$userId}");
// ৩. Counter — View Count, Like Count
Redis::incr("product:{$productId}:views");
Redis::incrby("product:{$productId}:likes", 1);
$views = Redis::get("product:{$productId}:views");
// ৪. Sorted Set — Leaderboard/Ranking
// Score যোগ করুন
Redis::zadd('product:sales', ['iPhone 15' => 1500, 'Samsung S24' => 1200]);
// Top 10 দেখুন (সবচেয়ে বেশি বিক্রি)
$topSellers = Redis::zrevrange('product:sales', 0, 9, 'WITHSCORES');
// ৫. Pub/Sub — Real-time Notification
// Publisher (Event হলে publish করুন)
Redis::publish('order.created', json_encode([
'order_id' => $order->id,
'user_id' => $order->user_id,
'total' => $order->total,
]));
// Subscriber (Background এ চালু থাকবে)
Redis::subscribe(['order.created'], function ($message) {
$data = json_decode($message);
// WebSocket দিয়ে User কে notify করুন
broadcast(new OrderCreatedEvent($data));
});
// ৬. Rate Limiter — Redis দিয়ে
class ApiRateLimiter
{
public function check(string $key, int $maxAttempts, int $decaySeconds): bool
{
$current = Redis::incr($key);
if ($current === 1) {
Redis::expire($key, $decaySeconds);
}
return $current <= $maxAttempts;
}
}
// Middleware এ ব্যবহার করুন
if (!$rateLimiter->check("api:{$userId}", 60, 60)) {
return response()->json(['message' => 'Too many requests'], 429);
}
🔄 PART 3 — SQL vs NoSQL কোনটা কখন?
php
// ✅ SQL ব্যবহার করুন যখন:
// - Data structure fixed এবং সম্পর্ক আছে
// - Financial data (Bank, Payment) — ACID দরকার
// - Complex Joins এবং Transactions দরকার
// - Data Integrity সবচেয়ে গুরুত্বপূর্ণ
// ✅ NoSQL ব্যবহার করুন যখন:
// - Data structure flexible বা unknown
// - Huge scale (লক্ষ লক্ষ record দ্রুত read/write)
// - JSON/Document store করতে হবে
// - Real-time বা Cache দরকার
Real Project এ দুটো একসাথে ব্যবহার:
php
class ProductService
{
// ১. প্রথমে Redis Cache চেক করুন
public function getProduct(int $id): array
{
$cacheKey = "product:{$id}";
// Cache এ আছে? → সরাসরি দিন (সবচেয়ে দ্রুত)
if ($cached = Redis::get($cacheKey)) {
return json_decode($cached, true);
}
// MySQL থেকে নিন → Cache এ রাখুন
$product = Product::with(['category', 'brand'])->findOrFail($id);
$productArray = $product->toArray();
Redis::setex($cacheKey, 3600, json_encode($productArray));
return $productArray;
}
// ২. Product View Track করুন MongoDB তে
public function trackView(int $productId, string $userId): void
{
// MongoDB তে analytics store করুন
ProductView::create([
'product_id' => $productId,
'user_id' => $userId,
'ip' => request()->ip(),
'user_agent' => request()->userAgent(),
'viewed_at' => now(),
'session' => [
'source' => request()->header('referer'),
'platform' => $this->detectPlatform(),
],
]);
// Redis এ view count বাড়ান
Redis::incr("product:{$productId}:views");
}
// ৩. Search এর জন্য Elasticsearch
public function search(string $query): Collection
{
// Elasticsearch এ full-text search
$results = Elasticsearch::search([
'index' => 'products',
'body' => [
'query' => [
'multi_match' => [
'query' => $query,
'fields' => ['name^3', 'description', 'tags^2'],
'fuzziness' => 'AUTO',
]
]
]
]);
// ID গুলো নিয়ে MySQL থেকে full data আনুন
$ids = collect($results['hits']['hits'])->pluck('_id');
return Product::whereIn('id', $ids)->with('category')->get();
}
}
🏗️ Database Design Best Practices
php
// ১. Naming Convention — সবসময় consistent রাখুন
// টেবিল: plural, snake_case
// products, order_items, product_categories
// Column: singular, snake_case
// user_id, created_at, is_active
// Foreign Key: table_name + _id
// category_id, brand_id, user_id
// ২. Soft Delete — পুরোপুরি মুছবেন না
class Product extends Model
{
use SoftDeletes; // deleted_at column এ date রাখে
// Trashed সহ query
Product::withTrashed()->find(1);
// শুধু Trashed
Product::onlyTrashed()->get();
// Restore করুন
Product::withTrashed()->find(1)->restore();
// সত্যিকার Delete
Product::withTrashed()->find(1)->forceDelete();
}
// ৩. Observer — Model Events Handle করুন
class ProductObserver
{
public function creating(Product $product): void
{
// Auto slug generate করুন
$product->slug = Str::slug($product->name);
}
public function updating(Product $product): void
{
// Stock কমলে Alert দিন
if ($product->isDirty('stock') && $product->stock < $product->min_stock) {
LowStockAlert::dispatch($product);
}
}
public function deleted(Product $product): void
{
// Cache clear করুন
Cache::forget("product:{$product->id}");
Cache::forget('popular_products');
}
}
// ৪. Scope — বারবার একই Query না লিখতে
class Product extends Model
{
// Global Scope — সবসময় apply হবে
protected static function booted(): void
{
static::addGlobalScope('active', function($query) {
$query->where('status', 'active');
});
}
// Local Scope — দরকার মতো ব্যবহার করুন
public function scopeInStock($query)
{
return $query->where('stock', '>', 0);
}
public function scopeFeatured($query)
{
return $query->where('is_featured', true);
}
public function scopePriceRange($query, $min, $max)
{
return $query->whereBetween('price', [$min, $max]);
}
}
// ব্যবহার:
$products = Product::inStock()
->featured()
->priceRange(500, 2000)
->get();
📝 সংক্ষেপে মনে রাখুন
SQL (MySQL/PostgreSQL) ব্যবহার করুন যখন ডেটার মধ্যে সম্পর্ক আছে এবং Transactions দরকার — যেমন Orders, Payments, Users। MongoDB ব্যবহার করুন যখন ডেটার Structure flexible — যেমন Products এর Variants, Analytics। Redis ব্যবহার করুন Cache, Session, Queue এবং Real-time এর জন্য — এটা সবচেয়ে দ্রুত।
Real Project এ তিনটাই একসাথে ব্যবহার করা হয় — MySQL মূল ডেটার জন্য, Redis speed এর জন্য, MongoDB flexible data এর জন্য।
Q:4 - Distributed Systems (consistency, replication)
Answer:
🌐 Distributed System কী?
Distributed System হলো এমন একটি সিস্টেম যেখানে একাধিক কম্পিউটার/সার্ভার একসাথে কাজ করে — কিন্তু বাইরে থেকে দেখলে মনে হয় একটাই সিস্টেম।
❌ Single Server (সমস্যা):
[সব User] → [একটা Server] → [একটা Database]
↑ এটা crash করলে সব শেষ!
✅ Distributed System (সমাধান):
[User] → [Load Balancer]
↓
┌─────────┬─────────┐
[Server 1] [Server 2] [Server 3]
↓ ↓ ↓
[DB Master] ←──────────────┘
↓
[DB Replica 1] [DB Replica 2]
🧩 PART 1 — CAP Theorem
Distributed System এর সবচেয়ে গুরুত্বপূর্ণ Theory
CAP Theorem বলে — একটা Distributed System একসাথে তিনটার মধ্যে মাত্র দুটো গ্যারান্টি দিতে পারে:
C = Consistency → সব Node এ একই Data দেখাবে
A = Availability → সবসময় Response পাবেন
P = Partition → Network ভাগ হলেও সিস্টেম চলবে
Tolerance
Consistency (C)
△
│
│
CA ───────┼──────── CP
│
│
│
──────────┼──────────
│
AP │
│
Partition (P) ──── Availability (A)
CA — MySQL, PostgreSQL
(Network Partition হলে বন্ধ হয়ে যায়)
CP — MongoDB, Redis, HBase
(Partition হলে Availability sacrifice করে)
AP — Cassandra, CouchDB
(Partition হলে Consistency sacrifice করে)
Real-world উদাহরণ:
php
// ধরুন আপনার E-commerce এ Payment হচ্ছে
// CP System (Bank/Payment) — Consistency বেশি জরুরি
// Network issue হলে → Transaction বন্ধ রাখো
// কারণ: ভুল balance দেখানো মানে বড় সমস্যা
// AP System (Product View Count) — Availability বেশি জরুরি
// Network issue হলে → পুরনো count দেখাও, পরে ঠিক করো
// কারণ: ১০০ এর জায়গায় ৯৮ দেখালে বড় ক্ষতি নেই
🔄 PART 2 — Consistency (সামঞ্জস্যতা)
Consistency Levels — কতটা সঠিক ডেটা দরকার?
Strong Consistency → সবচেয়ে নতুন Data — ধীর
Eventual Consistency → একটু পুরনো হতে পারে — দ্রুত
Weak Consistency → পুরনো Data দিতে পারে — সবচেয়ে দ্রুত
Laravel-এ Consistency Implement করা:
১. Strong Consistency — Critical Data এর জন্য:
php
// Payment বা Financial Transaction — সবসময় Master DB পড়ুন
class PaymentService
{
public function processPayment(Order $order, array $paymentData): Payment
{
return DB::transaction(function () use ($order, $paymentData) {
// Master DB থেকে পড়ুন — সবচেয়ে নতুন data
// Replica থেকে নয়! Stale data বিপজ্জনক
$user = User::lockForUpdate()->findOrFail($order->user_id);
$wallet = Wallet::lockForUpdate()
->where('user_id', $user->id)
->firstOrFail();
// Balance চেক করুন
if ($wallet->balance < $order->total) {
throw new InsufficientBalanceException(
"Balance কম। প্রয়োজন: {$order->total}, আছে: {$wallet->balance}"
);
}
// Balance কাটুন
$wallet->decrement('balance', $order->total);
// Payment Record তৈরি করুন
$payment = Payment::create([
'order_id' => $order->id,
'user_id' => $user->id,
'amount' => $order->total,
'status' => 'completed',
'reference_no' => $this->generateReference(),
]);
// Order Status Update করুন
$order->update(['status' => 'paid', 'payment_id' => $payment->id]);
return $payment;
});
}
}
// Config — Critical Query সবসময় Master এ
class CriticalController extends Controller
{
public function sensitiveAction(Request $request)
{
// Sticky Session — এই request এর সব query Master এ যাবে
DB::statement('SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE');
$result = DB::transaction(function() use ($request) {
return $this->paymentService->process($request->all());
});
return response()->json($result);
}
}
২. Eventual Consistency — Non-critical Data এর জন্য:
php
// Product View Count — একটু পুরনো হলেও সমস্যা নেই
class ProductViewController
{
public function track(int $productId): void
{
// Redis এ দ্রুত increment করুন (Eventual)
// Database এ সাথে সাথে না গিয়ে পরে Sync হবে
Redis::incr("product:{$productId}:views");
Redis::expire("product:{$productId}:views", 86400);
// Queue এ Job দিন — পরে DB Sync হবে
SyncProductViews::dispatch($productId)
->delay(now()->addMinutes(5));
}
}
// Background Job — Redis থেকে DB তে Sync
class SyncProductViews implements ShouldQueue
{
public function __construct(private int $productId) {}
public function handle(): void
{
$views = Redis::get("product:{$this->productId}:views") ?? 0;
Product::where('id', $this->productId)
->update(['view_count' => DB::raw("view_count + {$views}")]);
// Redis reset করুন
Redis::del("product:{$this->productId}:views");
}
}
৩. Read-Your-Writes Consistency:
php
// Problem: User profile update করলো,
// কিন্তু Replica থেকে পুরনো data দেখাচ্ছে!
class UserProfileService
{
public function updateProfile(User $user, array $data): User
{
// Master এ update করুন
$user->update($data);
// Session এ flag রাখুন — এই user সবে update করেছে
session(['read_from_master_until' => now()->addSeconds(5)]);
return $user;
}
public function getProfile(int $userId): User
{
// Update করার পর ৫ সেকেন্ড Master থেকে পড়ুন
if (session('read_from_master_until') > now()) {
return DB::connection('mysql_master')
->table('users')
->where('id', $userId)
->first();
}
// তারপর Replica থেকে পড়ুন (দ্রুত)
return User::find($userId);
}
}
📋 PART 3 — Replication (প্রতিলিপি)
Replication কী?
একই ডেটা একাধিক সার্ভারে রাখা — যাতে একটা বন্ধ হলেও বাকিগুলো চলে।
Master-Slave Replication:
[Write] → [Master DB] ──→ [Slave/Replica 1] ← [Read]
└──→ [Slave/Replica 2] ← [Read]
└──→ [Slave/Replica 3] ← [Read]
Master: শুধু Write
Slave: শুধু Read (অনেক বেশি Read Traffic সামলাতে পারে)
Laravel-এ Master-Slave Replication:
php
// config/database.php
'mysql' => [
'driver' => 'mysql',
// Write — সব INSERT, UPDATE, DELETE এখানে যাবে
'write' => [
'host' => env('DB_MASTER_HOST', '10.0.0.1'),
'port' => env('DB_MASTER_PORT', 3306),
],
// Read — সব SELECT এখানে যাবে (Load Balance হবে)
'read' => [
['host' => env('DB_REPLICA_1_HOST', '10.0.0.2')],
['host' => env('DB_REPLICA_2_HOST', '10.0.0.3')],
['host' => env('DB_REPLICA_3_HOST', '10.0.0.4')],
],
'sticky' => true, // Write এর পর সাথে সাথে Read করলে Master থেকে পড়বে
'database' => env('DB_DATABASE'),
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
],
// আলাদা Connection — Critical Read এর জন্য
'mysql_master' => [
'driver' => 'mysql',
'host' => env('DB_MASTER_HOST'),
'database' => env('DB_DATABASE'),
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
],
Replication Lag Handle করা:
php
// Problem: Master এ write করলাম,
// কিন্তু Replica তে এখনো পৌঁছায়নি!
// এই সময়কে বলে Replication Lag
class OrderService
{
public function createOrder(array $data): Order
{
// Master এ Order তৈরি করুন
$order = Order::create($data);
// Replication Lag এড়াতে — ID Cache এ রাখুন
Cache::put(
"order_exists:{$order->id}",
true,
now()->addSeconds(10) // ১০ সেকেন্ড — Lag এর চেয়ে বেশি
);
return $order;
}
public function getOrder(int $orderId): Order
{
// সবে তৈরি হয়েছে? Master থেকে পড়ুন
if (Cache::has("order_exists:{$orderId}")) {
return DB::connection('mysql_master')
->table('orders')
->where('id', $orderId)
->first();
}
// পুরনো Order — Replica থেকে পড়ুন
return Order::find($orderId);
}
}
🔀 PART 4 — Sharding (ডেটা ভাগ করা)
Sharding কী?
যখন একটা DB Server এ ডেটা অনেক বেশি হয়ে যায়, তখন ডেটাকে ভাগ করে একাধিক সার্ভারে রাখা হয়।
Without Sharding:
[১ কোটি User] → [একটা DB] ← অনেক Slow!
With Sharding:
User ID 1-25 লক্ষ → [DB Shard 1]
User ID 25-50 লক্ষ → [DB Shard 2]
User ID 50-75 লক্ষ → [DB Shard 3]
User ID 75-100 লক্ষ → [DB Shard 4]
Laravel-এ Sharding Implement করা:
php
// app/Services/ShardingService.php
class ShardingService
{
// Shard গুলোর Connection name
private array $shards = [
'db_shard_1',
'db_shard_2',
'db_shard_3',
'db_shard_4',
];
// User ID দিয়ে কোন Shard এ যাবে নির্ধারণ করুন
public function getShardForUser(int $userId): string
{
$shardIndex = $userId % count($this->shards);
return $this->shards[$shardIndex];
}
// Geographic Sharding — দেশ অনুযায়ী
public function getShardForCountry(string $countryCode): string
{
return match(true) {
in_array($countryCode, ['BD', 'IN', 'PK']) => 'db_shard_asia',
in_array($countryCode, ['US', 'CA']) => 'db_shard_na',
in_array($countryCode, ['GB', 'DE', 'FR']) => 'db_shard_eu',
default => 'db_shard_global',
};
}
}
// Sharded User Repository
class ShardedUserRepository
{
public function __construct(
private ShardingService $sharding
) {}
public function find(int $userId): ?object
{
$shard = $this->sharding->getShardForUser($userId);
return DB::connection($shard)
->table('users')
->where('id', $userId)
->first();
}
public function create(array $data): object
{
// User তৈরি করুন — ID আগে জানি না
// তাই সব Shard এ sequentially try করুন
$userId = $this->generateUserId();
$shard = $this->sharding->getShardForUser($userId);
DB::connection($shard)->table('users')->insert([
'id' => $userId,
'name' => $data['name'],
'email' => $data['email'],
'created_at' => now(),
]);
return $this->find($userId);
}
// Cross-Shard Query — সব Shard এ Search করুন
public function searchByEmail(string $email): ?object
{
foreach ($this->sharding->getAllShards() as $shard) {
$user = DB::connection($shard)
->table('users')
->where('email', $email)
->first();
if ($user) return $user;
}
return null;
}
// Parallel Cross-Shard Query — দ্রুত Version
public function searchAllShards(string $query): Collection
{
$results = collect();
$jobs = [];
// সব Shard এ একসাথে Query পাঠান
foreach ($this->sharding->getAllShards() as $shard) {
$jobs[] = SearchShard::dispatch($shard, $query);
}
// Result collect করুন
// (Real implementation এ Promise/Async ব্যবহার করুন)
return $results;
}
}
🔒 PART 5 — Distributed Locking
সমস্যা — Race Condition:
দুজন User একসাথে শেষ ১টা Product কিনতে চাইছে:
User A: Stock Check → ১ আছে ✓
User B: Stock Check → ১ আছে ✓ ← একই সময়ে!
User A: Order Create → Stock 0
User B: Order Create → Stock -1 ← বিপদ!
Solution — Distributed Lock দিয়ে:
php
// app/Services/DistributedLockService.php
class DistributedLockService
{
private Redis $redis;
private string $lockPrefix = 'lock:';
// Lock নিন
public function acquire(
string $resource,
int $ttlSeconds = 30
): ?string {
$lockKey = $this->lockPrefix . $resource;
$lockToken = Str::uuid()->toString(); // Unique token
// SET NX — শুধু তখনই set হবে যদি না থাকে
$acquired = Redis::set(
$lockKey,
$lockToken,
'EX', $ttlSeconds, // TTL
'NX' // Only if Not Exists
);
return $acquired ? $lockToken : null;
}
// Lock ছাড়ুন — শুধু যে নিয়েছে সেই ছাড়তে পারবে
public function release(string $resource, string $token): bool
{
$lockKey = $this->lockPrefix . $resource;
// Lua Script — Atomic operation
$script = "
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
";
return (bool) Redis::eval($script, 1, $lockKey, $token);
}
// Lock সহ কাজ করুন — Automatic release
public function withLock(
string $resource,
callable $callback,
int $ttl = 30
): mixed {
$token = $this->acquire($resource, $ttl);
if (!$token) {
throw new LockNotAcquiredException(
"Resource '{$resource}' is currently locked. Please try again."
);
}
try {
return $callback();
} finally {
$this->release($resource, $token);
}
}
}
// ব্যবহার — Product Purchase এ
class PurchaseController extends Controller
{
public function __construct(
private DistributedLockService $lockService,
private OrderService $orderService
) {}
public function purchase(Request $request)
{
$productId = $request->product_id;
try {
// Product এর জন্য Lock নিন
$result = $this->lockService->withLock(
"product:purchase:{$productId}",
function () use ($request, $productId) {
// Lock এর ভেতরে — এখন একজনই আছে
$product = Product::lockForUpdate()
->findOrFail($productId);
if ($product->stock < $request->quantity) {
throw new InsufficientStockException(
"মাত্র {$product->stock}টা বাকি আছে"
);
}
return $this->orderService->create([
'product_id' => $productId,
'quantity' => $request->quantity,
'user_id' => auth()->id(),
]);
},
ttl: 10 // ১০ সেকেন্ডের মধ্যে শেষ করতে হবে
);
return response()->json([
'success' => true,
'order' => $result,
]);
} catch (LockNotAcquiredException $e) {
return response()->json([
'success' => false,
'message' => 'এই মুহূর্তে অনেক চাপ। একটু পরে try করুন।',
], 429);
} catch (InsufficientStockException $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 422);
}
}
}
📨 PART 6 — Distributed Message Queue
Queue কেন দরকার Distributed System এ?
সমস্যা — Services সরাসরি কথা বললে:
[Order Service] → HTTP → [Email Service]
↑ যদি down থাকে?
→ Order fail হয়ে যাবে!
সমাধান — Queue দিয়ে:
[Order Service] → [Queue] → [Email Service]
↑ Email Service down হলেও
Queue এ থাকবে, পরে পাঠাবে
php
// app/Jobs/ProcessOrderJob.php
class ProcessOrderJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
// Retry Configuration
public int $tries = 3; // ৩ বার try করবে
public int $timeout = 60; // ৬০ সেকেন্ডের বেশি না
public int $backoff = 5; // Retry এর আগে ৫ সেকেন্ড অপেক্ষা
public function __construct(
private Order $order
) {}
public function handle(
EmailService $emailService,
InventoryService $inventoryService,
NotificationService $notificationService
): void {
// ১. Inventory Update করুন
$inventoryService->reduceStock(
$this->order->product_id,
$this->order->quantity
);
// ২. Email পাঠান
$emailService->sendOrderConfirmation($this->order);
// ৩. Push Notification পাঠান
$notificationService->notify(
$this->order->user_id,
"আপনার Order #{$this->order->id} confirmed!"
);
// ৪. Analytics Update করুন
SyncOrderAnalytics::dispatch($this->order)
->onQueue('analytics') // আলাদা Queue
->delay(now()->addMinutes(1));
}
// সব Retry ব্যর্থ হলে
public function failed(\Throwable $exception): void
{
Log::error('Order processing failed', [
'order_id' => $this->order->id,
'error' => $exception->getMessage(),
]);
// Admin কে Alert করুন
AdminAlert::dispatch(
"Order #{$this->order->id} processing failed!",
$exception->getMessage()
);
// Order Status Update করুন
$this->order->update(['status' => 'processing_failed']);
}
}
// Priority Queue — জরুরি কাজ আগে
class OrderController extends Controller
{
public function store(Request $request)
{
$order = Order::create($request->validated());
// High Priority — দ্রুত process হবে
ProcessOrderJob::dispatch($order)
->onQueue('high-priority');
// Normal Priority
GenerateInvoice::dispatch($order)
->onQueue('default');
// Low Priority — পরে হলেও চলবে
UpdateRecommendations::dispatch($order)
->onQueue('low-priority');
return response()->json($order, 201);
}
}
// Queue Worker চালান — আলাদা আলাদা Priority
// php artisan queue:work --queue=high-priority,default,low-priority
🏥 PART 7 — Health Check & Fault Tolerance
php
// app/Http/Controllers/HealthController.php
class HealthController extends Controller
{
public function check(): JsonResponse
{
$checks = [
'database' => $this->checkDatabase(),
'redis' => $this->checkRedis(),
'queue' => $this->checkQueue(),
'disk' => $this->checkDisk(),
];
$allHealthy = collect($checks)->every(
fn($check) => $check['status'] === 'healthy'
);
return response()->json([
'status' => $allHealthy ? 'healthy' : 'degraded',
'timestamp' => now()->toIso8601String(),
'services' => $checks,
], $allHealthy ? 200 : 503);
}
private function checkDatabase(): array
{
try {
$start = microtime(true);
DB::select('SELECT 1');
$responseTime = round((microtime(true) - $start) * 1000, 2);
return [
'status' => 'healthy',
'response_time' => "{$responseTime}ms",
];
} catch (\Exception $e) {
return ['status' => 'unhealthy', 'error' => $e->getMessage()];
}
}
private function checkRedis(): array
{
try {
$start = microtime(true);
Redis::ping();
$responseTime = round((microtime(true) - $start) * 1000, 2);
return [
'status' => 'healthy',
'response_time' => "{$responseTime}ms",
];
} catch (\Exception $e) {
return ['status' => 'unhealthy', 'error' => $e->getMessage()];
}
}
private function checkQueue(): array
{
$failedJobs = DB::table('failed_jobs')->count();
return [
'status' => $failedJobs < 10 ? 'healthy' : 'degraded',
'failed_jobs' => $failedJobs,
];
}
private function checkDisk(): array
{
$freeSpace = disk_free_space('/');
$totalSpace = disk_total_space('/');
$usedPercent = round((1 - $freeSpace / $totalSpace) * 100, 1);
return [
'status' => $usedPercent < 85 ? 'healthy' : 'critical',
'used_percent' => "{$usedPercent}%",
'free_space' => $this->formatBytes($freeSpace),
];
}
}
// Circuit Breaker — Service বার বার fail করলে বন্ধ করুন
class CircuitBreaker
{
private string $service;
private int $threshold; // কতবার fail হলে open হবে
private int $timeout; // কতক্ষণ open থাকবে
// States: closed (normal), open (blocked), half-open (testing)
public function call(callable $action): mixed
{
$state = $this->getState();
// Open — Service বন্ধ, সরাসরি Error দিন
if ($state === 'open') {
if ($this->shouldAttemptReset()) {
$this->setState('half-open');
} else {
throw new ServiceUnavailableException(
"{$this->service} is currently unavailable"
);
}
}
try {
$result = $action();
$this->onSuccess(); // Closed State এ ফিরুন
return $result;
} catch (\Exception $e) {
$this->onFailure(); // Failure Count বাড়ান
throw $e;
}
}
private function onSuccess(): void
{
Redis::del("circuit:{$this->service}:failures");
$this->setState('closed');
}
private function onFailure(): void
{
$failures = Redis::incr("circuit:{$this->service}:failures");
Redis::expire("circuit:{$this->service}:failures", $this->timeout);
if ($failures >= $this->threshold) {
$this->setState('open');
Log::warning("Circuit Breaker opened for {$this->service}");
}
}
private function getState(): string
{
return Redis::get("circuit:{$this->service}:state") ?? 'closed';
}
private function setState(string $state): void
{
Redis::setex(
"circuit:{$this->service}:state",
$this->timeout,
$state
);
}
}
// ব্যবহার
class ExternalApiService
{
public function __construct(
private CircuitBreaker $breaker
) {}
public function getData(): array
{
return $this->breaker->call(function () {
$response = Http::timeout(5)
->get('https://external-api.com/data');
if ($response->failed()) {
throw new \Exception('API call failed');
}
return $response->json();
});
}
}
📝 সংক্ষেপে মনে রাখুন
Distributed Systems এর মূল বিষয়গুলো হলো —
CAP Theorem — Consistency, Availability, Partition Tolerance — যেকোনো দুটো বেছে নিতে হবে।
Consistency — Critical data (Payment) তে Strong, Non-critical (View Count) তে Eventual।
Replication — Master Write করে, Replica Read করে — Performance ও Availability দুটোই বাড়ে।
Sharding — ডেটা অনেক বেশি হলে ভাগ করে রাখুন।
Distributed Lock — Race Condition এড়াতে Redis Lock ব্যবহার করুন।
Circuit Breaker — একটা Service বারবার fail করলে বাকি সিস্টেম রক্ষা করুন।
Laravel এ Redis, Queue, Database Read/Write Split ব্যবহার করে এই সব Concept সহজেই Implement করা যায়।
Q:5 - Caching (Redis, Memcached)
Answer:
⚡ Caching কী?
Caching হলো ঘন ঘন ব্যবহৃত ডেটা দ্রুত অ্যাক্সেসের জন্য কাছে রেখে দেওয়া। প্রতিবার Database এ না গিয়ে Cache থেকে ডেটা নিলে সিস্টেম অনেক দ্রুত হয়।
❌ Without Cache:
[User Request] → [Laravel] → [Database] → [Response]
↑ প্রতিবার DB Hit = ধীর, ব্যয়বহুল
✅ With Cache:
[User Request] → [Laravel] → [Cache] → [Response] ← দ্রুত!
↓ না থাকলে
[Database] → [Cache তে রাখো] → [Response]
Cache কতটা দ্রুত?
Database Query → 10-100ms
Redis Cache → 0.1-1ms ← ১০০ গুণ দ্রুত!
Memcached → 0.1-1ms ← ১০০ গুণ দ্রুত!
Memory (Array) → 0.001ms ← সবচেয়ে দ্রুত কিন্তু Persistent না
🔧 PART 1 — Laravel Cache Setup
Redis Setup:
bash
# Redis Install করুন
composer require predis/predis
# অথবা PHP Redis Extension
pecl install redis
php
// .env
CACHE_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_DB=0 // Cache এর জন্য
REDIS_CACHE_DB=1 // আলাদা DB — Session এর সাথে মিশবে না
// config/cache.php
'redis' => [
'driver' => 'redis',
'connection' => 'cache',
'lock_connection' => 'default',
],
// config/database.php — Redis Connections
'redis' => [
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0, // Queue & General
],
'cache' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 1, // Cache আলাদা DB তে
],
'session' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 2, // Session আলাদা DB তে
],
],
Memcached Setup:
bashcomposer require ext-memcached
php
// .env
CACHE_DRIVER=memcached
MEMCACHED_HOST=127.0.0.1
MEMCACHED_PORT=11211
// config/cache.php
'memcached' => [
'driver' => 'memcached',
'servers' => [
[
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
'port' => env('MEMCACHED_PORT', 11211),
'weight' => 100,
],
// Multiple Servers — Load Distribution
[
'host' => env('MEMCACHED_HOST_2', '10.0.0.2'),
'port' => 11211,
'weight' => 100,
],
],
],
🎯 PART 2 — Cache Strategies (কৌশল)
Strategy 1 — Cache-Aside (Lazy Loading)
সবচেয়ে জনপ্রিয় Pattern। দরকার হলে Cache এ রাখা হয়।
php
// app/Services/ProductService.php
class ProductService
{
// ১. Basic Cache Remember
public function getProduct(int $id): Product
{
return Cache::remember(
"product:{$id}", // Cache Key
now()->addHours(6), // Expiry — ৬ ঘন্টা
fn() => Product::with(['category', 'brand', 'images'])
->findOrFail($id)
);
}
// ২. Cache Forever — খুব কম পরিবর্তন হয় এমন Data
public function getCategories(): Collection
{
return Cache::rememberForever(
'all_categories',
fn() => Category::where('is_active', true)
->orderBy('name')
->get()
);
}
// ৩. Cache Tags — Related Cache একসাথে Clear করুন
public function getProductsByCategory(int $categoryId): Collection
{
return Cache::tags(['products', "category:{$categoryId}"])
->remember(
"products:category:{$categoryId}",
now()->addHours(2),
fn() => Product::where('category_id', $categoryId)
->where('status', 'active')
->with(['brand', 'images'])
->orderByDesc('created_at')
->get()
);
}
// ৪. Stale-While-Revalidate — Cache Expire হলেও পুরনো দেখাও
// Background এ নতুন করো
public function getPopularProducts(): Collection
{
$cacheKey = 'popular_products';
// Cache আছে? → দিন
if ($cached = Cache::get($cacheKey)) {
// Cache প্রায় শেষ? → Background এ refresh করুন
$ttl = Cache::get("{$cacheKey}:ttl");
if ($ttl && $ttl < now()->addMinutes(5)->timestamp) {
RefreshPopularProductsCache::dispatch();
}
return $cached;
}
// Cache নেই → DB থেকে নিন
return $this->refreshPopularProductsCache();
}
public function refreshPopularProductsCache(): Collection
{
$products = Product::withCount('orders')
->orderByDesc('orders_count')
->take(20)
->get();
Cache::put('popular_products', $products, now()->addHour());
Cache::put('popular_products:ttl', now()->addHour()->timestamp, now()->addHour());
return $products;
}
}
Strategy 2 — Write-Through Cache
Data লেখার সময় একসাথে Cache ও Database Update করা।
php
class ProductRepository
{
// Create — DB ও Cache একসাথে
public function create(array $data): Product
{
$product = Product::create($data);
// সাথে সাথে Cache এ রাখুন
Cache::put(
"product:{$product->id}",
$product->load(['category', 'brand']),
now()->addHours(6)
);
// Category Cache Clear করুন
Cache::tags(["category:{$product->category_id}"])->flush();
Cache::forget('popular_products');
return $product;
}
// Update — DB ও Cache একসাথে
public function update(int $id, array $data): Product
{
$product = Product::findOrFail($id);
$oldCategoryId = $product->category_id;
$product->update($data);
$product->refresh()->load(['category', 'brand', 'images']);
// Cache Update করুন
Cache::put("product:{$id}", $product, now()->addHours(6));
// Related Cache Clear করুন
Cache::tags(["category:{$oldCategoryId}"])->flush();
if ($product->category_id !== $oldCategoryId) {
Cache::tags(["category:{$product->category_id}"])->flush();
}
return $product;
}
// Delete — DB ও Cache একসাথে
public function delete(int $id): bool
{
$product = Product::findOrFail($id);
// Cache গুলো Clear করুন
Cache::forget("product:{$id}");
Cache::tags(["category:{$product->category_id}", 'products'])->flush();
return $product->delete();
}
}
Strategy 3 — Write-Behind Cache (Lazy Write)
Cache এ লিখুন — Database এ পরে Sync করুন। অনেক দ্রুত।
php
// Counter বা Analytics এর জন্য দারুণ
class AnalyticsService
{
// ১. View Count — Cache এ রাখুন, পরে DB Sync
public function incrementView(int $productId): void
{
$key = "product:{$productId}:views:pending";
// Redis এ Increment করুন — DB তে যাবে না এখন
Redis::incr($key);
Redis::expire($key, 3600);
// ৫ মিনিট পর DB Sync করুন
SyncViewCount::dispatch($productId)
->delay(now()->addMinutes(5))
->onQueue('analytics');
}
// ২. Like/Dislike — Batch এ DB তে লিখুন
public function toggleLike(int $productId, int $userId): array
{
$likeKey = "product:{$productId}:likes";
$dislikeKey = "product:{$productId}:dislikes";
$userLikeKey = "user:{$userId}:liked_products";
// User আগে Like দিয়েছে?
$hasLiked = Redis::sismember($userLikeKey, $productId);
if ($hasLiked) {
// Unlike করুন
Redis::srem($userLikeKey, $productId);
Redis::decr($likeKey);
$action = 'unliked';
} else {
// Like করুন
Redis::sadd($userLikeKey, $productId);
Redis::incr($likeKey);
$action = 'liked';
}
// Background এ DB Sync করুন
SyncProductLikes::dispatch($productId, $userId, $action)
->onQueue('analytics');
return [
'action' => $action,
'likes' => (int) Redis::get($likeKey),
];
}
}
// Background Sync Job
class SyncViewCount implements ShouldQueue
{
public function __construct(private int $productId) {}
public function handle(): void
{
$key = "product:{$this->productId}:views:pending";
$views = Redis::getdel($key); // Get and Delete atomically
if ($views) {
Product::where('id', $this->productId)
->increment('view_count', (int) $views);
}
}
}
🏗️ PART 3 — Cache Key Design (গুরুত্বপূর্ণ!)
php
// app/Services/CacheKeyService.php
class CacheKey
{
// ভালো Cache Key Design — Hierarchical
const VERSION = 'v1'; // Schema change হলে version বাড়ান
// Product Keys
public static function product(int $id): string
{
return self::VERSION . ":product:{$id}";
}
public static function productList(array $filters = []): string
{
$filterHash = md5(serialize($filters)); // Filters এর Hash
return self::VERSION . ":products:list:{$filterHash}";
}
public static function productsByCategory(int $catId, int $page = 1): string
{
return self::VERSION . ":products:cat:{$catId}:page:{$page}";
}
// User Keys
public static function userProfile(int $userId): string
{
return self::VERSION . ":user:{$userId}:profile";
}
public static function userCart(int $userId): string
{
return self::VERSION . ":user:{$userId}:cart";
}
public static function userPermissions(int $userId): string
{
return self::VERSION . ":user:{$userId}:permissions";
}
// Config Keys
public static function siteSettings(): string
{
return self::VERSION . ":config:site_settings";
}
}
// ব্যবহার
Cache::remember(CacheKey::product($id), 3600, fn() => Product::find($id));
Cache::remember(CacheKey::userCart($userId), 1800, fn() => Cart::forUser($userId));
// ❌ ভুল Cache Key Design
Cache::remember('product', ...); // Collision!
Cache::remember("product_$id", ...); // Version নেই
Cache::remember("p_{$id}", ...); // বোঝা যাচ্ছে না
🔴 PART 4 — Redis Advanced Features
Redis Data Structures — Laravel এ:
php
// ১. String — Simple Value Store
Redis::set('app:version', '2.5.1');
Redis::setex('otp:01712345678', 300, '123456'); // ৫ মিনিট
$otp = Redis::get('otp:01712345678');
// ২. Hash — Object Store (User Session এর মতো)
Redis::hmset("cart:{$userId}", [
'items_count' => 5,
'total' => 2500,
'coupon' => 'SAVE20',
'updated_at' => now()->timestamp,
]);
// একটা field update
Redis::hset("cart:{$userId}", 'items_count', 6);
// সব field পড়ুন
$cart = Redis::hgetall("cart:{$userId}");
// ৩. List — Queue / Recent Items
// Left Push — নতুন item সামনে
Redis::lpush("user:{$userId}:recent_products", $productId);
// List এর size limit — শুধু ১০টা রাখুন
Redis::ltrim("user:{$userId}:recent_products", 0, 9);
// সব recent products
$recentProducts = Redis::lrange("user:{$userId}:recent_products", 0, -1);
// ৪. Set — Unique Values (Tags, Followers)
// Product Tags
Redis::sadd("product:{$productId}:tags", 'smartphone', 'apple', '5g');
// User Wishlist
Redis::sadd("user:{$userId}:wishlist", $productId);
Redis::srem("user:{$userId}:wishlist", $productId); // Remove
// Wishlist এ আছে?
$inWishlist = Redis::sismember("user:{$userId}:wishlist", $productId);
// Common Products (Intersection) — দুজনের Common Wishlist
$commonWishlist = Redis::sinter(
"user:{$user1Id}:wishlist",
"user:{$user2Id}:wishlist"
);
// ৫. Sorted Set — Leaderboard / Ranking
// Sales Score যোগ করুন
Redis::zadd('leaderboard:products:weekly', [
'iPhone 15' => 1500,
'Samsung S24' => 1200,
'MacBook Pro' => 800,
]);
// Score বাড়ান
Redis::zincrby('leaderboard:products:weekly', 10, 'iPhone 15');
// Top 10 Products
$topProducts = Redis::zrevrange(
'leaderboard:products:weekly',
0, 9,
'WITHSCORES'
);
// Rank জানুন
$rank = Redis::zrevrank('leaderboard:products:weekly', 'iPhone 15');
// ৬. HyperLogLog — Unique Visitor Count (Memory দক্ষ)
// প্রতিটা Visitor এর IP যোগ করুন
Redis::pfadd("page:{$pageId}:visitors:today", $request->ip());
// Approximate unique count (৯৯.৫% accurate, মাত্র ১২KB memory!)
$uniqueVisitors = Redis::pfcount("page:{$pageId}:visitors:today");
// ৭. Pub/Sub — Real-time Notification
// Publisher
Redis::publish('notifications', json_encode([
'user_id' => $userId,
'message' => 'আপনার Order Confirm হয়েছে!',
'type' => 'order_confirmed',
]));
// Subscriber (Artisan Command হিসেবে চালান)
Redis::subscribe(['notifications'], function ($message) {
$data = json_decode($message, true);
// WebSocket দিয়ে User কে পাঠান
broadcast(new UserNotification($data));
});
Redis Pipeline — Multiple Commands একসাথে:
php
// ❌ ধীর — প্রতিটা Command আলাদা Network Trip
Redis::set('key1', 'value1');
Redis::set('key2', 'value2');
Redis::set('key3', 'value3');
Redis::expire('key1', 3600);
Redis::expire('key2', 3600);
// ✅ দ্রুত — একটাই Network Trip এ সব
Redis::pipeline(function ($pipe) {
$pipe->set('key1', 'value1');
$pipe->set('key2', 'value2');
$pipe->set('key3', 'value3');
$pipe->expire('key1', 3600);
$pipe->expire('key2', 3600);
});
// Real Example — User Login Cache
public function cacheUserData(User $user): void
{
Redis::pipeline(function ($pipe) use ($user) {
// User Profile
$pipe->setex(
"user:{$user->id}:profile",
3600,
json_encode($user->toArray())
);
// Permissions
$pipe->setex(
"user:{$user->id}:permissions",
3600,
json_encode($user->getAllPermissions()->pluck('name'))
);
// Last Login Update
$pipe->hset(
"user:{$user->id}:meta",
'last_login',
now()->timestamp
);
// Online Status
$pipe->setex("user:{$user->id}:online", 300, 1);
});
}
Redis Lua Script — Atomic Operations:
php
// Atomic Stock Deduction — Race Condition ছাড়া
class AtomicStockService
{
public function deductStock(int $productId, int $quantity): bool
{
$script = "
local stock = tonumber(redis.call('get', KEYS[1]))
if stock == nil then
return -1 -- Stock Cache নেই
end
if stock < tonumber(ARGV[1]) then
return 0 -- Stock কম
end
redis.call('decrby', KEYS[1], ARGV[1])
return 1 -- সফল
";
$result = Redis::eval(
$script,
1, // Key count
"product:{$productId}:stock", // KEYS[1]
$quantity // ARGV[1]
);
return match($result) {
1 => true,
0 => throw new InsufficientStockException(),
-1 => $this->deductFromDatabase($productId, $quantity),
};
}
}
🔵 PART 5 — Memcached
Redis vs Memcached কখন কোনটা?
Redis Memcached
───────────────────── ─────────────────────
Data Structures আছে vs শুধু Key-Value
Persistence আছে vs Memory Only (Restart = Data হারায়)
Pub/Sub আছে vs নেই
Replication আছে vs নেই
বেশি Features vs Simple ও দ্রুত
Cache + Queue + Session vs শুধু Cache
কখন Memcached বেছে নেবেন:
✓ শুধু Simple Caching দরকার
✓ Multi-threaded Performance দরকার
✓ Very Large Cache (কয়েক TB)
✓ Simple Setup চান
php
// Memcached Operations — Laravel এ একই Cache Facade
Cache::store('memcached')->put('key', 'value', 3600);
Cache::store('memcached')->get('key');
Cache::store('memcached')->forget('key');
// Multiple Servers — Consistent Hashing
// config/cache.php
'memcached' => [
'driver' => 'memcached',
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
'sasl' => [
env('MEMCACHED_USERNAME'),
env('MEMCACHED_PASSWORD'),
],
'options' => [
// Consistent Hashing — Server যোগ/বাদ দিলে কম Cache Miss
Memcached::OPT_DISTRIBUTION => Memcached::DISTRIBUTION_CONSISTENT,
Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
],
'servers' => [
['host' => '10.0.0.1', 'port' => 11211, 'weight' => 100],
['host' => '10.0.0.2', 'port' => 11211, 'weight' => 100],
['host' => '10.0.0.3', 'port' => 11211, 'weight' => 100],
],
],
🛡️ PART 6 — Cache Problems & Solutions
১. Cache Stampede (Thunder Herd Problem):
সমস্যা: Cache Expire হলো → হাজারো Request একসাথে DB তে গেলো → DB Crash!
Cache Expire!
↓
[1000 Requests] ──→ [DB] ← অনেক চাপ!
php
// Solution 1 — Mutex/Lock ব্যবহার করুন
class CacheStampedeProtection
{
public function remember(string $key, int $ttl, callable $callback): mixed
{
// Cache আছে? দিন
if ($value = Cache::get($key)) {
return $value;
}
// Lock নিন — শুধু একজন DB তে যাবে
$lock = Cache::lock("lock:{$key}", 10);
try {
if ($lock->get()) {
// আমি Lock পেয়েছি — DB থেকে নিই
$value = $callback();
Cache::put($key, $value, $ttl);
return $value;
}
} finally {
$lock->release();
}
// Lock পাইনি — একটু অপেক্ষা করে আবার চেক করুন
sleep(1);
return Cache::get($key) ?? $callback();
}
}
// Solution 2 — Probabilistic Early Expiration
class EarlyExpirationCache
{
public function get(string $key, int $ttl, callable $callback): mixed
{
$cached = Cache::get($key);
if ($cached) {
// TTL এর ১০% বাকি থাকলে ১% chance এ early refresh
$remainingTtl = Redis::ttl($key);
$shouldRefresh = $remainingTtl < ($ttl * 0.1)
&& random_int(1, 100) === 1;
if (!$shouldRefresh) {
return $cached;
}
}
$value = $callback();
Cache::put($key, $value, $ttl);
return $value;
}
}
২. Cache Penetration (DB Attack):
সমস্যা: Hacker বারবার Non-existent Key দিয়ে Request করছে
→ Cache Miss → DB Hit → DB Overwhelmed!
GET /api/products/99999999 ← এই Product নেই!
GET /api/products/88888888 ← এটাও নেই!
... লক্ষবার!
php
// Solution — Null Value Cache করুন
class ProductService
{
public function getProduct(int $id): ?Product
{
$cacheKey = "product:{$id}";
$cached = Cache::get($cacheKey);
// Null String মানে আগে চেক করা হয়েছে, নেই
if ($cached === 'NULL') {
return null;
}
if ($cached !== null) {
return $cached;
}
// DB তে খুঁজুন
$product = Product::find($id);
if ($product) {
Cache::put($cacheKey, $product, now()->addHours(6));
} else {
// নেই → Null Cache করুন — ৫ মিনিটের জন্য
Cache::put($cacheKey, 'NULL', now()->addMinutes(5));
}
return $product;
}
}
// Bloom Filter — আরো ভালো Solution
// সব Valid Product ID Redis এ রাখুন
class BloomFilterService
{
public function productExists(int $productId): bool
{
// Bloom Filter Check — O(1) time
return Redis::getbit("bloom:products", $productId % 10000000);
}
public function addProduct(int $productId): void
{
Redis::setbit("bloom:products", $productId % 10000000, 1);
}
}
// Middleware তে ব্যবহার করুন
public function getProduct(int $id, BloomFilterService $bloom): JsonResponse
{
// Bloom Filter এ নেই → সরাসরি 404, DB তে যাবে না
if (!$bloom->productExists($id)) {
return response()->json(['message' => 'Not found'], 404);
}
return response()->json($this->productService->getProduct($id));
}
৩. Cache Avalanche (একসাথে অনেক Cache Expire):
সমস্যা: রাত ১২টায় সব Cache Expire হলো
→ সব Request DB তে গেলো → DB Down!
php
// Solution — TTL তে Random Jitter যোগ করুন
class CacheService
{
// সব Cache একসাথে Expire না হওয়ার জন্য
public function rememberWithJitter(
string $key,
int $baseTtl,
callable $callback,
int $jitterPercent = 20
): mixed {
// Base TTL এর ±20% Random করুন
$jitter = (int) ($baseTtl * $jitterPercent / 100);
$ttl = $baseTtl + random_int(-$jitter, $jitter);
return Cache::remember($key, $ttl, $callback);
}
}
// ব্যবহার
// ৩৬০০ সেকেন্ড (±২০%) = ২৮৮০ থেকে ৪৩২০ সেকেন্ডের মধ্যে যেকোনো সময় expire
$product = $cacheService->rememberWithJitter(
"product:{$id}",
3600,
fn() => Product::find($id)
);
---
📊 **PART 7 — Full Cache Layer — Real Project**
php
// app/Services/CacheLayerService.php
// সব ধরনের Cache একটা Service এ
class CacheLayerService
{
// L1: Array Cache (Request scope — সবচেয়ে দ্রুত)
private array $requestCache = [];
// L2: Redis (Application scope)
// L3: Database (সবচেয়ে ধীর)
public function getProduct(int $id): ?Product
{
// L1 — Same Request এ আগে দেখা? Array থেকে নিন
if (isset($this->requestCache["product:{$id}"])) {
return $this->requestCache["product:{$id}"];
}
// L2 — Redis Cache তে আছে?
$product = Cache::remember(
CacheKey::product($id),
now()->addHours(6),
function () use ($id) {
// L3 — Database থেকে নিন
return Product::with(['category', 'brand', 'images'])
->find($id);
}
);
// L1 এ রাখুন — এই Request এ আবার লাগলে দ্রুত পাবো
$this->requestCache["product:{$id}"] = $product;
return $product;
}
// Dashboard Cache — Multiple Data একসাথে
public function getDashboardData(int $userId): array
{
return Cache::remember(
"dashboard:user:{$userId}",
now()->addMinutes(15),
function () use ($userId) {
// সব Data একসাথে Load করুন
return [
'total_orders' => Order::where('user_id', $userId)->count(),
'pending_orders' => Order::where('user_id', $userId)
->where('status', 'pending')->count(),
'total_spent' => Order::where('user_id', $userId)
->where('status', 'completed')
->sum('total'),
'wishlist_count' => Wishlist::where('user_id', $userId)->count(),
'recent_orders' => Order::where('user_id', $userId)
->with('items.product')
->latest()
->take(5)
->get(),
'recommended' => $this->getRecommendations($userId),
];
}
);
}
// Cache Warming — Deploy এর পরে Cache গরম করুন
public function warmCache(): void
{
// Popular Products Cache করুন
$popularProducts = Product::withCount('orders')
->orderByDesc('orders_count')
->take(100)
->get();
foreach ($popularProducts as $product) {
Cache::put(
CacheKey::product($product->id),
$product->load(['category', 'brand']),
now()->addHours(6)
);
}
// Categories Cache করুন
Cache::put(
'all_categories',
Category::where('is_active', true)->get(),
now()->addDay()
);
Log::info('Cache warmed successfully', [
'products_cached' => $popularProducts->count()
]);
}
}
// Artisan Command — Deploy এর পরে Cache Warm করুন
class WarmCacheCommand extends Command
{
protected $signature = 'cache:warm';
protected $description = 'Warm application cache after deployment';
public function handle(CacheLayerService $cacheService): void
{
$this->info('Cache warming শুরু হচ্ছে...');
$start = microtime(true);
$cacheService->warmCache();
$time = round(microtime(true) - $start, 2);
$this->info("✅ Cache warming সম্পন্ন! সময় লেগেছে: {$time}s");
}
}
// চালান: php artisan cache:warm
---
📈 **PART 8 — Cache Monitoring**
php
// Cache Hit Rate Monitor করুন
class CacheMonitorMiddleware
{
public function handle(Request $request, Closure $next): Response
{
// Cache Event Listen করুন
Event::listen(CacheHit::class, function ($event) {
Redis::incr('metrics:cache:hits');
});
Event::listen(CacheMissed::class, function ($event) {
Redis::incr('metrics:cache:misses');
});
return $next($request);
}
}
// Cache Stats দেখুন
class CacheStatsController extends Controller
{
public function stats(): JsonResponse
{
$hits = (int) Redis::get('metrics:cache:hits') ?? 0;
$misses = (int) Redis::get('metrics:cache:misses') ?? 0;
$total = $hits + $misses;
$info = Redis::info('memory');
return response()->json([
'hit_rate' => $total > 0
? round($hits / $total * 100, 2) . '%'
: '0%',
'total_hits' => $hits,
'total_misses'=> $misses,
'memory_used' => $info['used_memory_human'],
'memory_peak' => $info['used_memory_peak_human'],
]);
}
}
---
📝 **সংক্ষেপে মনে রাখুন**
**Cache কেন** — Database চাপ কমাতে এবং Response দ্রুত করতে।
**Redis** — Cache ছাড়াও Queue, Session, Pub/Sub, Leaderboard সব করা যায় — Laravel এ সবচেয়ে জনপ্রিয়।
**Memcached** — শুধু Simple Caching দরকার হলে এবং Multi-threaded Performance দরকার হলে।
**Cache Strategies** — Cache-Aside সবচেয়ে জনপ্রিয়, Write-Through Critical Data এর জন্য, Write-Behind Analytics এর জন্য।
**সমস্যাগুলো** — Stampede (Lock দিয়ে), Penetration (Null Cache দিয়ে), Avalanche (Jitter দিয়ে) সমাধান করুন।
Real Project এ **Redis** ব্যবহার করুন — এটাই যথেষ্ট। Cache Key Design ভালো রাখুন এবং Cache Invalidation সতর্কভাবে করুন।
**_Q:6_** - Security (OAuth2, JWT, encryption)
**Answer:**
🔐 **Security কেন গুরুত্বপূর্ণ?**
একটা Security Breach এর পরিণাম:
├── User এর Personal Data চুরি
├── Financial Loss
├── Company এর Reputation নষ্ট
├── Legal Penalty (GDPR Fine)
└── Service Down
বাস্তব উদাহরণ:
2013 → Adobe: 38 মিলিয়ন User Data চুরি
2016 → Uber: 57 মিলিয়ন User Data চুরি
2021 → Facebook: 533 মিলিয়ন User Data Leak
---
🔵 **PART 1 — Authentication vs Authorization**
Authentication (প্রমাণ) Authorization (অনুমতি)
──────────────────────── ────────────────────────
"তুমি কে?" vs "তুমি কী করতে পারবে?"
Login করা vs Admin Panel Access
Password Check vs Role Permission Check
JWT Token Verify vs Policy Check
উদাহরণ:
আপনি Bank এ গেলেন → Authentication (NID দেখালেন)
Locker Room এ গেলেন → Authorization (আপনার Locker নম্বর আছে)
---
🟡 **PART 2 — JWT (JSON Web Token)**
**JWT কী?**
JWT হলো একটা **Self-contained Token** — যেখানে User এর তথ্য **Encoded** থাকে। Server কে Database এ যেতে হয় না Token Verify করতে।
JWT Structure — তিনটা Part, Dot দিয়ে আলাদা:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ← Header (Base64)
.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6InRlc3Q ← Payload (Base64)
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV ← Signature (HMAC)
Header: {"alg": "HS256", "typ": "JWT"}
Payload: {"user_id": 1, "email": "test@example.com", "exp": 1700000000}
Signature: HMACSHA256(header + payload, SECRET_KEY)
**Laravel-এ JWT Implementation:**
bash
composer require php-open-source-saver/jwt-auth
php artisan vendor:publish --provider="PHPOpenSourceSaver\JWTAuth\Providers\LaravelServiceProvider"
php artisan jwt:secret
php
// .env
JWT_SECRET=your-super-secret-key-minimum-32-characters
JWT_TTL=60 // Access Token — ৬০ মিনিট
JWT_REFRESH_TTL=20160 // Refresh Token — ১৪ দিন
// config/auth.php
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
**JWT Auth Controller — Complete Implementation:**
php
// app/Http/Controllers/Api/AuthController.php
class AuthController extends Controller
{
// ১. Register
public function register(RegisterRequest $request): JsonResponse
{
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password),
]);
// Token Generate করুন
$token = auth('api')->login($user);
return $this->respondWithToken($token, $user, 201);
}
// ২. Login
public function login(LoginRequest $request): JsonResponse
{
$credentials = $request->only('email', 'password');
// Brute Force Protection
if ($this->hasTooManyLoginAttempts($request)) {
return response()->json([
'message' => 'অনেকবার চেষ্টা করেছেন। ১৫ মিনিট পরে try করুন।',
'retry_after' => $this->limiter->availableIn(
$this->throttleKey($request)
),
], 429);
}
if (!$token = auth('api')->attempt($credentials)) {
// Failed Attempt Count বাড়ান
$this->incrementLoginAttempts($request);
return response()->json([
'message' => 'Email বা Password ভুল।',
], 401);
}
// Success — Attempt Reset করুন
$this->clearLoginAttempts($request);
// Login Log রাখুন
$this->logLoginActivity($request);
return $this->respondWithToken($token);
}
// ৩. Logout
public function logout(): JsonResponse
{
// Token Blacklist এ রাখুন
auth('api')->logout();
return response()->json([
'message' => 'সফলভাবে Logout হয়েছেন।'
]);
}
// ৪. Token Refresh
public function refresh(): JsonResponse
{
try {
$newToken = auth('api')->refresh();
return $this->respondWithToken($newToken);
} catch (TokenExpiredException $e) {
return response()->json([
'message' => 'Refresh Token Expired। আবার Login করুন।'
], 401);
}
}
// ৫. Current User
public function me(): JsonResponse
{
$user = auth('api')->user();
return response()->json([
'user' => new UserResource($user),
]);
}
// Token Response Format
private function respondWithToken(
string $token,
?User $user = null,
int $status = 200
): JsonResponse {
$user ??= auth('api')->user();
return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
'expires_in' => auth('api')->factory()->getTTL() * 60,
'user' => new UserResource($user),
], $status);
}
// Login Activity Log
private function logLoginActivity(Request $request): void
{
LoginActivity::create([
'user_id' => auth('api')->id(),
'ip_address' => $request->ip(),
'user_agent' => $request->userAgent(),
'location' => $this->getLocationFromIp($request->ip()),
'logged_at' => now(),
]);
}
}
**JWT Middleware — Token Verify করুন:**
php
// app/Http/Middleware/JwtMiddleware.php
class JwtMiddleware
{
public function handle(Request $request, Closure $next): Response
{
try {
// Token থেকে User নিন
$user = JWTAuth::parseToken()->authenticate();
if (!$user) {
return response()->json([
'message' => 'User not found'
], 401);
}
// User Active আছে?
if (!$user->is_active) {
return response()->json([
'message' => 'আপনার Account Suspended।'
], 403);
}
} catch (TokenExpiredException $e) {
return response()->json([
'message' => 'Token Expired। Refresh করুন।',
'error' => 'token_expired',
], 401);
} catch (TokenInvalidException $e) {
return response()->json([
'message' => 'Token Invalid।',
'error' => 'token_invalid',
], 401);
} catch (JWTException $e) {
return response()->json([
'message' => 'Token দেওয়া হয়নি।',
'error' => 'token_absent',
], 401);
}
return $next($request);
}
}
**Access Token + Refresh Token Pattern:**
php
// app/Services/TokenService.php
class TokenService
{
// Short-lived Access Token + Long-lived Refresh Token
public function generateTokenPair(User $user): array
{
// Access Token — ১৫ মিনিট
$accessToken = JWTAuth::customClaims([
'type' => 'access',
'user_id' => $user->id,
])->fromUser($user);
// Refresh Token — ৩০ দিন (DB তে Store করুন)
$refreshToken = Str::random(64);
// DB তে Refresh Token Store করুন
RefreshToken::create([
'user_id' => $user->id,
'token' => hash('sha256', $refreshToken), // Hash করে রাখুন
'expires_at' => now()->addDays(30),
'ip_address' => request()->ip(),
]);
return [
'access_token' => $accessToken,
'refresh_token' => $refreshToken,
'expires_in' => 900, // ১৫ মিনিট
];
}
// Refresh Token দিয়ে নতুন Access Token নিন
public function refreshAccessToken(string $refreshToken): array
{
$hashedToken = hash('sha256', $refreshToken);
$storedToken = RefreshToken::where('token', $hashedToken)
->where('expires_at', '>', now())
->whereNull('revoked_at')
->with('user')
->firstOrFail();
// পুরনো Refresh Token Revoke করুন (Rotation)
$storedToken->update(['revoked_at' => now()]);
// নতুন Token Pair Generate করুন
return $this->generateTokenPair($storedToken->user);
}
// Logout — সব Token Revoke করুন
public function revokeAllTokens(int $userId): void
{
RefreshToken::where('user_id', $userId)
->whereNull('revoked_at')
->update(['revoked_at' => now()]);
JWTAuth::invalidate(JWTAuth::getToken());
}
}
---
🟠 **PART 3 — OAuth2**
**OAuth2 কী?**
OAuth2 হলো **Third-party Authorization** এর Standard। "Google দিয়ে Login" বা "Facebook দিয়ে Login" — এটাই OAuth2।
OAuth2 Flow:
[User] → "Google দিয়ে Login" → [Your App]
↓
[Google Auth Server]
↓ (User Allow করলো)
Authorization Code
↓
[Your App এ Code পাঠালো]
↓
Code দিয়ে Access Token নিন
↓
Access Token দিয়ে User Info নিন
↓
[User Logged In!]
**Laravel Passport — Full OAuth2 Server:**
bash
composer require laravel/passport
php artisan passport:install
php artisan passport:keys
php
// app/Models/User.php
class User extends Authenticatable
{
use HasApiTokens; // Passport এর জন্য
}
// app/Providers/AppServiceProvider.php
public function boot(): void
{
// Token Expiry Configure করুন
Passport::tokensExpireIn(now()->addMinutes(15));
Passport::refreshTokensExpireIn(now()->addDays(30));
Passport::personalAccessTokensExpireIn(now()->addMonths(6));
// Scopes Define করুন — Permission Level
Passport::tokensCan([
'read-products' => 'Products দেখার অনুমতি',
'write-products' => 'Products তৈরি/edit করার অনুমতি',
'read-orders' => 'Orders দেখার অনুমতি',
'write-orders' => 'Orders তৈরি করার অনুমতি',
'admin' => 'সব কিছু করার অনুমতি',
]);
}
**OAuth2 Client Credentials (Machine to Machine):**
php
php
// routes/api.php
Route::middleware('client')->group(function () {
// শুধু Authenticated Client Access করতে পারবে
Route::get('/internal/products', [InternalProductController::class, 'index']);
Route::post('/internal/sync', [SyncController::class, 'sync']);
});
// Client Credentials Grant — Service to Service Auth
class InternalSyncService
{
private ?string $accessToken = null;
public function getToken(): string
{
// Cache করুন — বারবার নেওয়া লাগবে না
if ($this->accessToken && !$this->isTokenExpired()) {
return $this->accessToken;
}
$response = Http::post(config('services.auth.url') . '/oauth/token', [
'grant_type' => 'client_credentials',
'client_id' => config('services.auth.client_id'),
'client_secret' => config('services.auth.client_secret'),
'scope' => 'read-products write-products',
]);
$this->accessToken = $response->json('access_token');
Cache::put(
'service_access_token',
$this->accessToken,
now()->addMinutes(14) // Expire এর ১ মিনিট আগে
);
return $this->accessToken;
}
public function syncProducts(): void
{
$token = $this->getToken();
Http::withToken($token)
->post(config('services.inventory.url') . '/internal/sync', [
'products' => Product::all()->toArray()
]);
}
}
**Social Login — Google/Facebook দিয়ে Login:**
php
bash
composer require laravel/socialite
shell
php
// config/services.php
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => env('GOOGLE_REDIRECT_URI'),
],
'facebook' => [
'client_id' => env('FACEBOOK_APP_ID'),
'client_secret' => env('FACEBOOK_APP_SECRET'),
'redirect' => env('FACEBOOK_REDIRECT_URI'),
],
// app/Http/Controllers/Auth/SocialAuthController.php
class SocialAuthController extends Controller
{
// ১. Google/Facebook এ Redirect করুন
public function redirect(string $provider): RedirectResponse
{
$validProviders = ['google', 'facebook', 'github'];
if (!in_array($provider, $validProviders)) {
return response()->json(['message' => 'Invalid provider'], 400);
}
return Socialite::driver($provider)->redirect();
}
// ২. Callback — Google/Facebook থেকে ফিরে আসলে
public function callback(string $provider): JsonResponse
{
try {
$socialUser = Socialite::driver($provider)->user();
} catch (\Exception $e) {
return response()->json([
'message' => 'Social Login ব্যর্থ হয়েছে।'
], 401);
}
// Email দিয়ে User খুঁজুন
$user = User::where('email', $socialUser->getEmail())->first();
if ($user) {
// আগে Register করেছে → Social Account Link করুন
$user->socialAccounts()->updateOrCreate(
['provider' => $provider],
[
'provider_id' => $socialUser->getId(),
'provider_token' => $socialUser->token,
'avatar' => $socialUser->getAvatar(),
]
);
} else {
// নতুন User → Register করুন
$user = DB::transaction(function () use ($socialUser, $provider) {
$newUser = User::create([
'name' => $socialUser->getName(),
'email' => $socialUser->getEmail(),
'email_verified_at' => now(), // Social Login = Email Verified
'password' => bcrypt(Str::random(32)), // Random Password
'avatar' => $socialUser->getAvatar(),
]);
$newUser->socialAccounts()->create([
'provider' => $provider,
'provider_id' => $socialUser->getId(),
'provider_token' => $socialUser->token,
]);
return $newUser;
});
}
// JWT Token Generate করুন
$token = auth('api')->login($user);
return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
'user' => new UserResource($user),
]);
}
}
---
🟢 **PART 4 — Encryption (এনক্রিপশন)**
**Encryption এর ধরন:**
php
Symmetric Encryption → একই Key দিয়ে Encrypt ও Decrypt
AES-256 — দ্রুত, Bulk Data এর জন্য
Asymmetric Encryption → Public Key দিয়ে Encrypt
Private Key দিয়ে Decrypt
RSA — ধীর, Key Exchange এর জন্য
Hashing → One-way, Decrypt করা যায় না
bcrypt, argon2 — Password Storage এর জন্য
**Laravel-এ Encryption:**
plaintext
php
// ১. Built-in Encryption — Sensitive Data
class SensitiveDataService
{
// Encrypt করুন — DB তে Store করার আগে
public function storeSensitiveData(array $data): UserProfile
{
return UserProfile::create([
'user_id' => auth()->id(),
// Encrypt করে রাখুন
'national_id' => encrypt($data['national_id']),
'bank_account' => encrypt($data['bank_account']),
'phone' => encrypt($data['phone']),
// Normal Data
'name' => $data['name'],
]);
}
// Decrypt করুন — ব্যবহারের সময়
public function getSensitiveData(int $userId): array
{
$profile = UserProfile::where('user_id', $userId)->firstOrFail();
return [
'name' => $profile->name,
'national_id' => decrypt($profile->national_id),
'bank_account' => decrypt($profile->bank_account),
'phone' => decrypt($profile->phone),
];
}
}
// ২. Model Casting — Automatic Encrypt/Decrypt
class UserProfile extends Model
{
protected $casts = [
// Laravel 9+ — Automatic Encrypt/Decrypt
'national_id' => 'encrypted',
'bank_account' => 'encrypted',
'metadata' => 'encrypted:array', // Array ও Encrypt করা যায়
];
}
// ব্যবহার — Automatic!
$profile = UserProfile::create([
'national_id' => '1234567890', // Automatically Encrypted
]);
echo $profile->national_id; // Automatically Decrypted → 1234567890
// ৩. Custom Encryption — AES-256
class EncryptionService
{
private string $key;
private string $cipher = 'AES-256-CBC';
public function __construct()
{
$this->key = base64_decode(config('app.encryption_key'));
}
public function encrypt(string $data): string
{
$iv = random_bytes(openssl_cipher_iv_length($this->cipher));
$encrypted = openssl_encrypt($data, $this->cipher, $this->key, 0, $iv);
$hmac = hash_hmac('sha256', $encrypted, $this->key, true);
// IV + HMAC + Encrypted Data একসাথে রাখুন
return base64_encode($iv . $hmac . $encrypted);
}
public function decrypt(string $encryptedData): string
{
$decoded = base64_decode($encryptedData);
$ivLength = openssl_cipher_iv_length($this->cipher);
$iv = substr($decoded, 0, $ivLength);
$hmac = substr($decoded, $ivLength, 32);
$encrypted = substr($decoded, $ivLength + 32);
// HMAC Verify করুন — Tamper Check
$expectedHmac = hash_hmac('sha256', $encrypted, $this->key, true);
if (!hash_equals($expectedHmac, $hmac)) {
throw new DecryptException('Data tampered! HMAC mismatch.');
}
return openssl_decrypt($encrypted, $this->cipher, $this->key, 0, $iv);
}
}
**Password Hashing — সঠিকভাবে:**
php
php
// ১. Password Store করুন — bcrypt (Default)
$user = User::create([
'password' => Hash::make($request->password), // bcrypt
]);
// অথবা Argon2 (আরো শক্তিশালী)
$hash = Hash::driver('argon2id')->make($request->password);
// ২. Password Verify করুন
if (!Hash::check($request->password, $user->password)) {
throw new UnauthorizedException('Password ভুল।');
}
// ৩. Password Rehash — Algorithm Update হলে
if (Hash::needsRehash($user->password)) {
$user->update([
'password' => Hash::make($request->password)
]);
}
// ❌ কখনো এভাবে করবেন না
$user->password = md5($password); // MD5 — ভাঙা!
$user->password = sha1($password); // SHA1 — ভাঙা!
$user->password = $password; // Plain text — মারাত্মক!
---
🛡️ **PART 5 — Authorization (Role & Permission)**
**Spatie Permission — Professional RBAC:**
php
bash
composer require spatie/laravel-permission
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
php artisan migrate
shell
php
// Roles ও Permissions তৈরি করুন
class RolePermissionSeeder extends Seeder
{
public function run(): void
{
// Permissions তৈরি করুন
$permissions = [
// Products
'products.view', 'products.create',
'products.edit', 'products.delete',
// Orders
'orders.view', 'orders.manage',
'orders.refund',
// Users
'users.view', 'users.create',
'users.edit', 'users.delete', 'users.ban',
// Settings
'settings.view', 'settings.manage',
];
foreach ($permissions as $permission) {
Permission::create(['name' => $permission]);
}
// Roles তৈরি করুন এবং Permissions দিন
$superAdmin = Role::create(['name' => 'super-admin']);
$superAdmin->givePermissionTo(Permission::all()); // সব Permission
$admin = Role::create(['name' => 'admin']);
$admin->givePermissionTo([
'products.view', 'products.create', 'products.edit',
'orders.view', 'orders.manage',
'users.view',
]);
$seller = Role::create(['name' => 'seller']);
$seller->givePermissionTo([
'products.view', 'products.create', 'products.edit',
'orders.view',
]);
$customer = Role::create(['name' => 'customer']);
$customer->givePermissionTo([
'products.view',
'orders.view',
]);
}
}
// Policy — Object Level Permission
class ProductPolicy
{
// Product দেখার অনুমতি
public function view(User $user, Product $product): bool
{
return $user->hasPermissionTo('products.view');
}
// Product Edit — নিজের বা Admin
public function update(User $user, Product $product): bool
{
return $user->id === $product->user_id
|| $user->hasRole(['admin', 'super-admin']);
}
// Product Delete — শুধু Admin
public function delete(User $user, Product $product): bool
{
return $user->hasRole(['admin', 'super-admin'])
|| ($user->id === $product->user_id
&& $user->hasPermissionTo('products.delete'));
}
}
// Controller এ ব্যবহার করুন
class ProductController extends Controller
{
public function update(UpdateProductRequest $request, Product $product)
{
// Policy Check — Permission নেই? Automatically 403
$this->authorize('update', $product);
$product->update($request->validated());
return new ProductResource($product);
}
public function destroy(Product $product)
{
$this->authorize('delete', $product);
$product->delete();
return response()->json(['message' => 'Product deleted']);
}
}
// Blade এ Permission Check
@can('products.create')
নতুন Product যোগ করুন
@endcan
@role('admin|super-admin')
Admin Panel
@endrole
---
🔒 **PART 6 — Common Security Vulnerabilities**
**SQL Injection থেকে রক্ষা:**
php
php
// ❌ ভুল — SQL Injection এর সুযোগ!
$products = DB::select(
"SELECT * FROM products WHERE name = '$request->name'"
);
// ✅ সঠিক — Parameterized Query
$products = DB::select(
'SELECT * FROM products WHERE name = ?',
[$request->name]
);
// ✅ আরো ভালো — Eloquent ORM
$products = Product::where('name', $request->name)->get();
**XSS (Cross-Site Scripting) থেকে রক্ষা:**
php
php
// ❌ ভুল — XSS এর সুযোগ!
return response("
Hello {$request->name}
");// ✅ সঠিক — Escape করুন
return response("
Hello " . e($request->name) . "
");// ✅ Blade Template — Automatically Escape
{{ $user->name }} // Safe — Escaped
{!! $user->bio !!} // Unsafe — Raw HTML
// Input Sanitize করুন
class SanitizeInput
{
public static function clean(string $input): string
{
// HTML Tags সরিয়ে দিন
$input = strip_tags($input);
// Special Characters Encode করুন
$input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
// Extra Whitespace সরান
return trim($input);
}
}
// Content Security Policy Header
class SecurityHeadersMiddleware
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$response->headers->set(
'Content-Security-Policy',
"default-src 'self'; " .
"script-src 'self' 'nonce-" . csrf_token() . "'; " .
"style-src 'self' https://fonts.googleapis.com; " .
"img-src 'self' data: https:; " .
"font-src 'self' https://fonts.gstatic.com;"
);
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-Frame-Options', 'DENY');
$response->headers->set('X-XSS-Protection', '1; mode=block');
$response->headers->set(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains'
);
return $response;
}
}
**CSRF Protection:**
php
php
// Laravel Automatically CSRF Protect করে Web Routes এ
// API Routes এ Stateless Token ব্যবহার করুন
// Exempt করুন — Webhook Routes
// app/Http/Middleware/VerifyCsrfToken.php
protected $except = [
'webhook/*',
'api/payment/callback',
];
// API এ Sanctum Token ব্যবহার করুন
Route::middleware('auth:sanctum')->group(function () {
Route::post('/products', [ProductController::class, 'store']);
});
**Rate Limiting — Advanced:**
php
php
// app/Providers/AppServiceProvider.php
public function boot(): void
{
// API Rate Limit
RateLimiter::for('api', function (Request $request) {
return $request->user()
? Limit::perMinute(120)->by($request->user()->id)
: Limit::perMinute(30)->by($request->ip());
});
// Login Rate Limit — Brute Force Protection
RateLimiter::for('login', function (Request $request) {
return [
Limit::perMinute(5)->by($request->input('email')),
Limit::perMinute(10)->by($request->ip()),
];
});
// OTP Rate Limit
RateLimiter::for('otp', function (Request $request) {
return Limit::perHour(3)->by($request->input('phone'));
});
// Expensive Operations
RateLimiter::for('reports', function (Request $request) {
return Limit::perHour(5)
->by($request->user()->id)
->response(function () {
return response()->json([
'message' => 'প্রতি ঘন্টায় সর্বোচ্চ ৫টা Report Generate করা যাবে।'
], 429);
});
});
}
---
🔏 **PART 7 — API Security Best Practices**
php
php
// app/Http/Middleware/ApiSecurityMiddleware.php
class ApiSecurityMiddleware
{
public function handle(Request $request, Closure $next): Response
{
// ১. HTTPS Check
if (!$request->secure() && config('app.env') === 'production') {
return response()->json([
'message' => 'HTTPS Required'
], 403);
}
// ২. API Version Check
$version = $request->header('X-API-Version', '1');
if (!in_array($version, ['1', '2'])) {
return response()->json([
'message' => 'Unsupported API Version'
], 400);
}
// ৩. Request Size Limit — Large Payload Attack থেকে রক্ষা
$contentLength = $request->header('Content-Length', 0);
if ($contentLength > 10 * 1024 * 1024) { // ১০ MB
return response()->json([
'message' => 'Request Too Large'
], 413);
}
// ৪. Suspicious Pattern Detect করুন
$body = $request->getContent();
if ($this->hasSqlInjection($body) || $this->hasXssPattern($body)) {
Log::warning('Suspicious request detected', [
'ip' => $request->ip(),
'url' => $request->fullUrl(),
]);
return response()->json([
'message' => 'Invalid Request'
], 400);
}
$response = $next($request);
// ৫. Sensitive Data Response Header সরিয়ে দিন
$response->headers->remove('X-Powered-By');
$response->headers->remove('Server');
$response->headers->set('X-Request-ID', Str::uuid());
return $response;
}
private function hasSqlInjection(string $input): bool
{
$patterns = [
'/(\bunion\b.*\bselect\b)/i',
'/(\bselect\b.*\bfrom\b)/i',
'/(\bdrop\b.*\btable\b)/i',
'/(\bdelete\b.*\bfrom\b)/i',
"/(--|\#|\/\*)/",
];
foreach ($patterns as $pattern) {
if (preg_match($pattern, $input)) {
return true;
}
}
return false;
}
private function hasXssPattern(string $input): bool
{
$patterns = [
'/<script\b[^>]*>/i',
'/javascript:/i',
'/on\w+\s*=/i',
];
foreach ($patterns as $pattern) {
if (preg_match($pattern, $input)) {
return true;
}
}
return false;
}
}
// Sensitive Data Masking — Log এ Sensitive Data রাখবেন না
class MaskSensitiveData
{
private array $sensitiveFields = [
'password', 'password_confirmation',
'credit_card', 'card_number', 'cvv',
'national_id', 'bank_account',
'token', 'secret', 'api_key',
];
public function mask(array $data): array
{
foreach ($data as $key => $value) {
if (in_array(strtolower($key), $this->sensitiveFields)) {
$data[$key] = '***MASKED***';
} elseif (is_array($value)) {
$data[$key] = $this->mask($value);
}
}
return $data;
}
}
---
🏗️ **PART 8 — Two Factor Authentication (2FA)**
php
php
// app/Services/TwoFactorService.php
class TwoFactorService
{
// ১. 2FA Enable করুন
public function enable(User $user): array
{
// Secret Key Generate করুন
$secret = Google2FA::generateSecretKey();
// Backup Codes Generate করুন
$backupCodes = collect(range(1, 8))
->map(fn() => Str::random(10))
->toArray();
// DB তে রাখুন (Encrypted)
$user->update([
'two_factor_secret' => encrypt($secret),
'two_factor_backup' => encrypt(json_encode(
array_map('bcrypt', $backupCodes)
)),
'two_factor_enabled' => false, // Confirm না হওয়া পর্যন্ত
]);
// QR Code URL Generate করুন
$qrCodeUrl = Google2FA::getQRCodeUrl(
config('app.name'),
$user->email,
$secret
);
return [
'secret' => $secret,
'qr_code_url' => $qrCodeUrl,
'backup_codes' => $backupCodes,
];
}
// ২. OTP Verify করুন
public function verify(User $user, string $otp): bool
{
$secret = decrypt($user->two_factor_secret);
// Google Authenticator OTP Check
$valid = Google2FA::verifyKey($secret, $otp, 1); // 1 = 30 sec window
if (!$valid) {
// Backup Code Check
$valid = $this->verifyBackupCode($user, $otp);
}
if ($valid && !$user->two_factor_enabled) {
$user->update(['two_factor_enabled' => true]);
}
return $valid;
}
// ৩. Login এ 2FA Check
public function challengeCheck(Request $request): JsonResponse
{
$user = auth('api')->user();
if (!$user->two_factor_enabled) {
return response()->json(['requires_2fa' => false]);
}
// 2FA Token পাঠান Session এ রাখুন
$challengeToken = Str::random(32);
Cache::put(
"2fa_challenge:{$challengeToken}",
$user->id,
now()->addMinutes(5)
);
// Current Token Invalidate করুন
auth('api')->logout();
return response()->json([
'requires_2fa' => true,
'challenge_token' => $challengeToken,
'message' => 'Google Authenticator থেকে OTP দিন।',
]);
}
}
---
📝 **সংক্ষেপে মনে রাখুন**
**JWT** — Stateless Authentication, Self-contained Token, Microservices এর জন্য আদর্শ। Access Token ছোট (১৫ মিনিট), Refresh Token বড় (৩০ দিন) রাখুন।
**OAuth2** — Third-party Login (Google, Facebook) এবং Machine-to-Machine Auth এর জন্য। Laravel Passport দিয়ে সহজে করা যায়।
**Encryption** — Sensitive Data (NID, Bank Account) সবসময় Encrypt করে রাখুন। Password সবসময় bcrypt/argon2 দিয়ে Hash করুন, কখনো Plain text না।
**Security Best Practices** — SQL Injection (Eloquent ORM), XSS (Escape), CSRF (Token), Rate Limiting, HTTPS — এই সবগুলো একসাথে মেনে চলুন।
**_Q:7_** - **DevOps (CI/CD, Docker, Kubernetes)**
**Answer**:
🚀 **DevOps কী?**
php
আগে (Traditional):
Developer → Code লিখলো → IT Team কে দিলো → ২ সপ্তাহ পরে Deploy!
↑ ধীর, Error প্রবণ
DevOps (এখন):
Developer → Code Push → Auto Test → Auto Build → Auto Deploy
↑ মিনিটের মধ্যে!
DevOps = Development + Operations
লক্ষ্য: দ্রুত, নিরাপদ, বারবার Software Deliver করা
---
🐳 **PART 1 — Docker**
**Docker কী?**
Docker হলো **Container** Technology। আপনার অ্যাপ এবং তার সব Dependencies একটা **Box (Container)** এ রেখে দেওয়া — যেকোনো Server এ একই ভাবে চলবে।
php
সমস্যা (Docker ছাড়া):
"আমার Machine এ কাজ করে কিন্তু
Server এ করে না!" ← সবচেয়ে বিখ্যাত সমস্যা 😅
সমাধান (Docker সহ):
Container এ সব আছে:
├── PHP 8.3
├── Laravel App
├── Composer Dependencies
├── Nginx Config
└── Environment Variables
→ যেকোনো জায়গায় একই রেজাল্ট!
VM vs Container:
┌─────────────────────┐ ┌─────────────────────┐
│ Virtual Machine │ │ Docker Container │
├─────────────────────┤ ├─────────────────────┤
│ Full OS (2-4 GB) │ │ App + Libs (50 MB) │
│ Slow Start (মিনিট) │ │ Fast Start (সেকেন্ড)│
│ Heavy │ │ Lightweight │
└─────────────────────┘ └─────────────────────┘
**Laravel Project এর জন্য Dockerfile:**
plaintext
dockerfile# Dockerfile
─────────────────────────────────────────
Stage 1: Dependencies Install
─────────────────────────────────────────
FROM php:8.3-fpm-alpine AS dependencies
System Dependencies Install করুন
RUN apk add --no-cache \
git \
curl \
libpng-dev \
libxml2-dev \
zip \
unzip \
nodejs \
npm
PHP Extensions Install করুন
RUN docker-php-ext-install \
pdo \
pdo_mysql \
mbstring \
exif \
pcntl \
bcmath \
gd \
opcache
Redis Extension
RUN pecl install redis && docker-php-ext-enable redis
Composer Install করুন
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
Working Directory Set করুন
WORKDIR /var/www/html
Dependencies Install করুন (Code আগে না)
এতে Docker Cache কাজ করে — দ্রুত Build
COPY composer.json composer.lock ./
RUN composer install \
--no-dev \
--no-interaction \
--prefer-dist \
--optimize-autoloader
NPM Dependencies
COPY package.json package-lock.json ./
RUN npm ci
─────────────────────────────────────────
Stage 2: Production Image
─────────────────────────────────────────
FROM php:8.3-fpm-alpine AS production
WORKDIR /var/www/html
Dependencies Stage থেকে নিন
COPY --from=dependencies /var/www/html/vendor ./vendor
COPY --from=dependencies /usr/local/lib/php /usr/local/lib/php
Application Code Copy করুন
COPY . .
Assets Build করুন
RUN npm run build && rm -rf node_modules
Laravel Optimization
RUN php artisan config:cache && \
php artisan route:cache && \
php artisan view:cache && \
php artisan event:cache
Permissions Set করুন
RUN chown -R www-data:www-data /var/www/html/storage \
/var/www/html/bootstrap/cache
Non-root User হিসেবে চালান — Security
USER www-data
Health Check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD php-fpm -t || exit 1
EXPOSE 9000
CMD ["php-fpm"]
**Docker Compose — Local Development:**
plaintext
yaml
docker-compose.yml
version: '3.8'
services:
# ─────────────────────────
# Laravel Application
# ─────────────────────────
app:
build:
context: .
dockerfile: Dockerfile
target: dependencies # Development এ dependencies stage
container_name: laravel_app
restart: unless-stopped
working_dir: /var/www/html
volumes:
- .:/var/www/html # Code Mount — Hot Reload
- ./storage:/var/www/html/storage
environment:
- APP_ENV=local
- APP_DEBUG=true
- DB_HOST=mysql
- REDIS_HOST=redis
- QUEUE_CONNECTION=redis
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
networks:
- laravel_network
# ─────────────────────────
# Nginx Web Server
# ─────────────────────────
nginx:
image: nginx:alpine
container_name: laravel_nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- .:/var/www/html
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./docker/nginx/ssl:/etc/nginx/ssl # SSL Certificates
depends_on:
- app
networks:
- laravel_network
# ─────────────────────────
# MySQL Database
# ─────────────────────────
mysql:
image: mysql:8.0
container_name: laravel_mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_USER: ${DB_USERNAME}
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql # Data Persist
- ./docker/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "3306:3306"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 20s
retries: 10
networks:
- laravel_network
# ─────────────────────────
# Redis Cache
# ─────────────────────────
redis:
image: redis:7-alpine
container_name: laravel_redis
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- laravel_network
# ─────────────────────────
# Queue Worker
# ─────────────────────────
queue:
build:
context: .
dockerfile: Dockerfile
target: dependencies
container_name: laravel_queue
restart: unless-stopped
command: php artisan queue:work redis \
--sleep=3 \
--tries=3 \
--timeout=90 \
--queue=high-priority,default,low-priority
volumes:
- .:/var/www/html
depends_on:
- app
- redis
networks:
- laravel_network
# ─────────────────────────
# Laravel Scheduler
# ─────────────────────────
scheduler:
build:
context: .
dockerfile: Dockerfile
target: dependencies
container_name: laravel_scheduler
restart: unless-stopped
command: >
sh -c "while true; do
php artisan schedule:run --verbose --no-interaction &
sleep 60
done"
volumes:
- .:/var/www/html
depends_on:
- app
- mysql
networks:
- laravel_network
# ─────────────────────────
# phpMyAdmin (Development)
# ─────────────────────────
phpmyadmin:
image: phpmyadmin/phpmyadmin
container_name: laravel_phpmyadmin
environment:
PMA_HOST: mysql
PMA_PORT: 3306
ports:
- "8080:80"
profiles:
- dev # শুধু Development এ
networks:
- laravel_network
─────────────────────────
Networks & Volumes
─────────────────────────
networks:
laravel_network:
driver: bridge
volumes:
mysql_data:
driver: local
redis_data:
driver: local
**Nginx Config:**
docker
nginx
docker/nginx/default.conf
server {
listen 80;
listen [::]:80;
server_name myapp.com www.myapp.com;
# HTTP → HTTPS Redirect
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name myapp.com www.myapp.com;
# SSL Configuration
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
root /var/www/html/public;
index index.php;
# Gzip Compression
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1000;
# Security Headers
add_header X-Frame-Options "DENY";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
# Static Files Cache
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# PHP Files
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass app:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
# Timeout Settings
fastcgi_connect_timeout 60;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
}
# Sensitive Files Hide করুন
location ~ /\.(env|git|htaccess) {
deny all;
}
# Max Upload Size
client_max_body_size 20M;
}
---
🔄 **PART 2 — CI/CD Pipeline**
**CI/CD কী?**
yaml
CI = Continuous Integration
→ Code Push করলেই Auto Test চলবে
CD = Continuous Delivery/Deployment
→ Test Pass হলে Auto Deploy হবে
পুরো Flow:
Developer → Git Push → CI Server
↓
[Code Checkout]
↓
[Dependencies Install]
↓
[Code Quality Check]
↓
[Unit Tests Run]
↓
[Integration Tests]
↓
[Docker Image Build]
↓
[Push to Registry]
↓
[Deploy to Staging]
↓
[Smoke Tests]
↓
[Deploy to Production] ✅
**GitHub Actions — CI/CD Pipeline:**
nginx
yaml
.github/workflows/ci-cd.yml
name: Laravel CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
PHP_VERSION: '8.3'
NODE_VERSION: '20'
DOCKER_IMAGE: ghcr.io/${{ github.repository }}
jobs:
# ─────────────────────────────────
# Job 1: Code Quality Check
# ─────────────────────────────────
code-quality:
name: 🔍 Code Quality
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_VERSION }}
extensions: mbstring, pdo, redis
coverage: xdebug
- name: Cache Composer
uses: actions/cache@v3
with:
path: vendor
key: composer-${{ hashFiles('composer.lock') }}
- name: Install Dependencies
run: composer install --no-interaction --prefer-dist
- name: PHP Code Style Check (Pint)
run: ./vendor/bin/pint --test
- name: Static Analysis (PHPStan)
run: ./vendor/bin/phpstan analyse --level=8
- name: Security Check
run: composer audit
# ─────────────────────────────────
# Job 2: Tests
# ─────────────────────────────────
tests:
name: 🧪 Run Tests
runs-on: ubuntu-latest
needs: code-quality
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: testing
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
redis:
image: redis:7-alpine
options: >-
--health-cmd="redis-cli ping"
--health-interval=10s
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_VERSION }}
extensions: mbstring, pdo_mysql, redis
coverage: xdebug
- name: Install Dependencies
run: composer install --no-interaction
- name: Copy .env
run: |
cp .env.testing.example .env.testing
php artisan key:generate --env=testing
- name: Run Migrations
run: php artisan migrate --env=testing --force
env:
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_DATABASE: testing
DB_USERNAME: root
DB_PASSWORD: secret
- name: Run Unit Tests
run: |
php artisan test \
--parallel \
--coverage-clover=coverage.xml \
--min=80
env:
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_DATABASE: testing
DB_USERNAME: root
DB_PASSWORD: secret
REDIS_HOST: 127.0.0.1
- name: Upload Coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
# ─────────────────────────────────
# Job 3: Build Docker Image
# ─────────────────────────────────
build:
name: 🐳 Build & Push Docker Image
runs-on: ubuntu-latest
needs: tests
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.DOCKER_IMAGE }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=sha,prefix=sha-
- name: Build & Push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
target: production
cache-from: type=gha
cache-to: type=gha,mode=max
# ─────────────────────────────────
# Job 4: Deploy to Staging
# ─────────────────────────────────
deploy-staging:
name: 🚀 Deploy to Staging
runs-on: ubuntu-latest
needs: build
environment: staging
steps:
- name: Deploy to Staging Server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.STAGING_HOST }}
username: ${{ secrets.STAGING_USER }}
key: ${{ secrets.STAGING_SSH_KEY }}
script: |
cd /var/www/staging
# নতুন Image Pull করুন
docker pull ${{ env.DOCKER_IMAGE }}:main
# Zero-downtime Deploy
docker-compose up -d --no-deps app
docker-compose exec -T app php artisan migrate --force
docker-compose exec -T app php artisan config:cache
docker-compose exec -T app php artisan route:cache
# Health Check
sleep 10
curl -f http://localhost/health || exit 1
echo "✅ Staging Deploy সফল!"
# ─────────────────────────────────
# Job 5: Deploy to Production
# ─────────────────────────────────
deploy-production:
name: 🌟 Deploy to Production
runs-on: ubuntu-latest
needs: deploy-staging
environment: production # Manual Approval লাগবে!
steps:
- name: Blue-Green Deploy to Production
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.PROD_HOST }}
username: ${{ secrets.PROD_USER }}
key: ${{ secrets.PROD_SSH_KEY }}
script: |
cd /var/www/production
# Current Version Backup
CURRENT=$(docker-compose ps -q app)
# নতুন Container চালু করুন (Blue-Green)
docker pull ${{ env.DOCKER_IMAGE }}:main
docker-compose up -d --no-deps --scale app=2 app
# Health Check
sleep 15
NEW_CONTAINER=$(docker-compose ps -q app | tail -1)
docker exec $NEW_CONTAINER curl -f http://localhost/health
# Migration Run করুন
docker-compose exec -T app php artisan migrate --force
# Cache Clear
docker-compose exec -T app php artisan optimize
# পুরনো Container সরিয়ে দিন
docker-compose up -d --no-deps --scale app=1 app
echo "✅ Production Deploy সফল!"
- name: Notify Slack
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: |
Production Deploy ${{ job.status }}!
Commit: ${{ github.sha }}
By: ${{ github.actor }}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
if: always()
**Test Classes — Laravel:**
plaintext
php
// tests/Feature/Api/ProductTest.php
class ProductApiTest extends TestCase
{
use RefreshDatabase, WithFaker;
private User $user;
private string $token;
protected function setUp(): void
{
parent::setUp();
// Test User তৈরি করুন
$this->user = User::factory()->create();
$this->token = JWTAuth::fromUser($this->user);
}
/** @test */
public function it_can_list_products(): void
{
Product::factory()->count(15)->create();
$response = $this->withToken($this->token)
->getJson('/api/v1/products');
$response->assertOk()
->assertJsonStructure([
'data' => [
'*' => ['id', 'name', 'price', 'stock']
],
'meta' => ['total', 'current_page']
])
->assertJsonCount(15, 'data');
}
/** @test */
public function it_can_create_product(): void
{
$category = Category::factory()->create();
$productData = [
'name' => 'Test Product',
'price' => 999.99,
'stock' => 50,
'category_id' => $category->id,
];
$response = $this->withToken($this->token)
->postJson('/api/v1/products', $productData);
$response->assertCreated()
->assertJsonFragment(['name' => 'Test Product']);
$this->assertDatabaseHas('products', [
'name' => 'Test Product',
'price' => 999.99,
]);
}
/** @test */
public function it_validates_required_fields(): void
{
$response = $this->withToken($this->token)
->postJson('/api/v1/products', []);
$response->assertUnprocessable()
->assertJsonValidationErrors(['name', 'price', 'stock']);
}
/** @test */
public function it_returns_404_for_nonexistent_product(): void
{
$response = $this->withToken($this->token)
->getJson('/api/v1/products/99999');
$response->assertNotFound();
}
}
---
☸️ **PART 3 — Kubernetes (K8s)**
**Kubernetes কী?**
Kubernetes হলো **Container Orchestration** Platform। অনেক Container কে Manage, Scale, ও Monitor করার Tool।
yaml
Docker = একটা Container চালানো
Kubernetes = হাজারো Container পরিচালনা করা
Kubernetes এর কাজ:
├── Auto Scaling → Traffic বাড়লে Container বাড়াও
├── Self Healing → Container Crash করলে নতুন তৈরি করো
├── Load Balancing → Traffic ভাগ করো
├── Rolling Update → Zero-downtime Deploy করো
└── Service Discovery→ Services খুঁজে পেতে সাহায্য করো
Kubernetes Architecture:
┌─────────────────────────────────────────────┐
│ Control Plane │
│ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │
│ │ API │ │Scheduler │ │ Controller │ │
│ │ Server │ │ │ │ Manager │ │
│ └──────────┘ └──────────┘ └─────────────┘ │
└─────────────────────────────────────────────┘
↓ Manages
┌─────────────────────────────────────────────┐
│ Worker Nodes │
│ ┌────────────────┐ ┌────────────────┐ │
│ │ Node 1 │ │ Node 2 │ │
│ │ ┌──────────┐ │ │ ┌──────────┐ │ │
│ │ │ Pod │ │ │ │ Pod │ │ │
│ │ │(Container│ │ │ │(Container│ │ │
│ │ │+Laravel) │ │ │ │+Laravel) │ │ │
│ │ └──────────┘ │ │ └──────────┘ │ │
│ └────────────────┘ └────────────────┘ │
└─────────────────────────────────────────────┘
**Laravel App এর জন্য Kubernetes Manifests:**
php
yaml
k8s/namespace.yml
apiVersion: v1
kind: Namespace
metadata:
name: laravel-app
labels:
app: laravel
plaintext
yaml
k8s/configmap.yml — Non-sensitive Configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: laravel-config
namespace: laravel-app
data:
APP_ENV: production
APP_DEBUG: "false"
LOG_CHANNEL: stderr
CACHE_DRIVER: redis
QUEUE_CONNECTION: redis
SESSION_DRIVER: redis
DB_CONNECTION: mysql
DB_PORT: "3306"
yaml
yaml
k8s/secret.yml — Sensitive Configuration
apiVersion: v1
kind: Secret
metadata:
name: laravel-secrets
namespace: laravel-app
type: Opaque
data:
# Base64 Encoded Values
APP_KEY: YmFzZTY0ZW5jb2RlZGtleQ==
DB_PASSWORD: c2VjcmV0cGFzc3dvcmQ=
REDIS_PASSWORD: cmVkaXNwYXNz
JWT_SECRET: and0c2VjcmV0a2V5
yaml
yaml
k8s/deployment.yml — Laravel App Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: laravel-app
namespace: laravel-app
labels:
app: laravel
version: "1.0"
spec:
replicas: 3 # ৩টা Instance চালাবে
selector:
matchLabels:
app: laravel
strategy:
type: RollingUpdate # Zero-downtime Update
rollingUpdate:
maxSurge: 1 # একটা বেশি তৈরি করো
maxUnavailable: 0 # কোনটা বন্ধ করো না
template:
metadata:
labels:
app: laravel
spec:
containers:
- name: laravel
image: ghcr.io/myapp/laravel:latest
imagePullPolicy: Always
ports:
- containerPort: 9000
# Environment Variables
envFrom:
- configMapRef:
name: laravel-config
- secretRef:
name: laravel-secrets
# Resource Limits — একটা Pod বেশি Resource নিতে পারবে না
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
# Health Checks
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/ready
port: 80
initialDelaySeconds: 10
periodSeconds: 5
# Lifecycle Hooks
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"] # Graceful shutdown
# Image Pull Secret
imagePullSecrets:
- name: ghcr-secret
yaml# k8s/hpa.yml — Auto Scaling
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: laravel-hpa
namespace: laravel-app
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: laravel-app
minReplicas: 3 # সর্বনিম্ন ৩টা
maxReplicas: 20 # সর্বোচ্চ ২০টা
metrics:
# CPU বেশি হলে Scale করুন
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # ৭০% CPU হলে নতুন Pod
# Memory বেশি হলে Scale করুন
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 60 # ১ মিনিট দেখো
policies:
- type: Pods
value: 3 # একসাথে ৩টা যোগ করো
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300 # ৫ মিনিট দেখো তারপর কমাও
yaml# k8s/queue-worker.yml — Queue Worker Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: laravel-queue
namespace: laravel-app
spec:
replicas: 2
template:
spec:
containers:
- name: queue-worker
image: ghcr.io/myapp/laravel:latest
command:
- php
- artisan
- queue:work
- redis
- --sleep=3
- --tries=3
- --timeout=90
- --queue=high-priority,default,low-priority
envFrom:
- configMapRef:
name: laravel-config
- secretRef:
name: laravel-secrets
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "250m"
# Queue Worker Health Check
livenessProbe:
exec:
command:
- php
- artisan
- queue:monitor
- --max=100
initialDelaySeconds: 60
periodSeconds: 60
yaml# k8s/service.yml — Service (Load Balancer)
apiVersion: v1
kind: Service
metadata:
name: laravel-service
namespace: laravel-app
spec:
selector:
app: laravel
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
k8s/ingress.yml — External Access
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: laravel-ingress
namespace: laravel-app
annotations:
# Nginx Ingress Controller
nginx.ingress.kubernetes.io/rewrite-target: /
# SSL Auto-renew
cert-manager.io/cluster-issuer: "letsencrypt-prod"
# Rate Limiting
nginx.ingress.kubernetes.io/rate-limit: "100"
nginx.ingress.kubernetes.io/rate-limit-window: "1m"
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.com
secretName: myapp-tls
rules:
- host: myapp.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: laravel-service
port:
number: 80
**Kubernetes Commands — Daily Use:**
yaml
bash
─────────────────────────────────────
Deploy করুন
─────────────────────────────────────
kubectl apply -f k8s/ # সব yaml apply করুন
kubectl apply -f k8s/deployment.yml
─────────────────────────────────────
Status দেখুন
─────────────────────────────────────
kubectl get pods -n laravel-app
kubectl get deployments -n laravel-app
kubectl get services -n laravel-app
kubectl get hpa -n laravel-app # Auto Scaling Status
─────────────────────────────────────
Logs দেখুন
─────────────────────────────────────
kubectl logs -f deployment/laravel-app -n laravel-app
kubectl logs -f pod/laravel-app-xxx -n laravel-app
─────────────────────────────────────
Pod এ ঢুকুন
─────────────────────────────────────
kubectl exec -it deployment/laravel-app \
-n laravel-app -- bash
Laravel Commands Run করুন
kubectl exec -it deployment/laravel-app \
-n laravel-app -- php artisan migrate
kubectl exec -it deployment/laravel-app \
-n laravel-app -- php artisan tinker
─────────────────────────────────────
Scale করুন
─────────────────────────────────────
kubectl scale deployment laravel-app \
--replicas=5 -n laravel-app
─────────────────────────────────────
Rolling Update — Zero Downtime
─────────────────────────────────────
kubectl set image deployment/laravel-app \
laravel=ghcr.io/myapp/laravel:v2.0 \
-n laravel-app
kubectl rollout status deployment/laravel-app -n laravel-app
─────────────────────────────────────
Rollback করুন — কিছু ভুল হলে
─────────────────────────────────────
kubectl rollout undo deployment/laravel-app -n laravel-app
kubectl rollout undo deployment/laravel-app \
--to-revision=3 -n laravel-app
─────────────────────────────────────
Resource Usage দেখুন
─────────────────────────────────────
kubectl top pods -n laravel-app
kubectl top nodes
🏥 **PART 4 — Health Check Endpoint**
yaml
php
// routes/api.php
Route::get('/health', [HealthController::class, 'check']);
Route::get('/health/ready', [HealthController::class, 'readiness']);
Route::get('/health/live', [HealthController::class, 'liveness']);
// app/Http/Controllers/HealthController.php
class HealthController extends Controller
{
// Liveness — Container জীবিত আছে?
public function liveness(): JsonResponse
{
return response()->json([
'status' => 'alive',
'timestamp' => now()->toIso8601String(),
]);
}
// Readiness — Traffic নেওয়ার জন্য Ready?
public function readiness(): JsonResponse
{
$checks = [];
$healthy = true;
// Database Check
try {
DB::select('SELECT 1');
$checks['database'] = 'ok';
} catch (\Exception $e) {
$checks['database'] = 'fail';
$healthy = false;
}
// Redis Check
try {
Redis::ping();
$checks['redis'] = 'ok';
} catch (\Exception $e) {
$checks['redis'] = 'fail';
$healthy = false;
}
// Queue Check
$failedJobs = DB::table('failed_jobs')->count();
$checks['queue'] = $failedJobs < 50 ? 'ok' : 'degraded';
return response()->json([
'status' => $healthy ? 'ready' : 'not_ready',
'checks' => $checks,
], $healthy ? 200 : 503);
}
}
---
📝 **সংক্ষেপে মনে রাখুন**
**Docker** — আপনার Laravel App এবং সব Dependencies একটা Container এ রাখুন। যেকোনো Server এ একই ভাবে চলবে। Local Development এ `docker-compose` ব্যবহার করুন।
**CI/CD** — GitHub Actions দিয়ে Code Push করলেই Auto Test, Build, Deploy হবে। Test ছাড়া Production এ যাবে না। এতে Human Error কমে।
**Kubernetes** — Production এ অনেক Container Manage করার জন্য। Auto Scaling, Self-healing, Zero-downtime Deploy — সব পাবেন। ছোট Project এ দরকার নেই, বড় হলে অপরিহার্য।
**শেখার ক্রম** — প্রথমে Docker শিখুন → তারপর GitHub Actions দিয়ে CI/CD → পরে Kubernetes।
**_Q:8_** - **Performance Optimization (profiling, load balancing)**
**Answer**:
⚡ **Performance কেন গুরুত্বপূর্ণ?**
গবেষণা বলছে:
shell
├── 1 সেকেন্ড Delay → ৭% Conversion কমে
├── 3 সেকেন্ড Delay → ৫৩% Mobile User চলে যায়
├── Google Ranking → Page Speed একটা Factor
└── Amazon: ১০০ms → ১% Revenue কমে
আদর্শ Response Time:
├── API Response → < 200ms ✅ খুব ভালো
├── Page Load → < 1s ✅ ভালো
├── Complex Report → < 3s ⚠️ গ্রহণযোগ্য
└── যেকোনো কিছু → > 5s ❌ User চলে যাবে
---
🔍 **PART 1 — Profiling (সমস্যা খুঁজে বের করা)**
**Profiling কী?**
Profiling মানে আপনার অ্যাপের **কোথায় সময় নষ্ট হচ্ছে** সেটা খুঁজে বের করা। Optimize করার আগে অবশ্যই Profile করুন।
php
❌ ভুল পদ্ধতি:
"মনে হচ্ছে Database Slow" → সেটা Optimize করলাম
→ কোনো পার্থক্য নেই!
✅ সঠিক পদ্ধতি:
Profile করুন → সমস্যা খুঁজুন → সেটা Fix করুন
→ আবার Profile করুন → Verify করুন
**Laravel Telescope — Development Profiling:**
plaintext
bash
composer require laravel/telescope --dev
php artisan telescope:install
php artisan migrate
plaintext
php
// config/telescope.php
return [
'enabled' => env('TELESCOPE_ENABLED', true),
// কী কী Track করবেন
'watchers' => [
Watchers\QueryWatcher::class => [
'enabled' => true,
'slow' => 100, // ১০০ms এর বেশি = Slow Query Alert
],
Watchers\RequestWatcher::class => [
'enabled' => true,
'size_limit' => env('TELESCOPE_RESPONSE_SIZE_LIMIT', 64),
],
Watchers\CacheWatcher::class => ['enabled' => true],
Watchers\JobWatcher::class => ['enabled' => true],
Watchers\MailWatcher::class => ['enabled' => true],
Watchers\ModelWatcher::class => [
'enabled' => true,
'events' => ['eloquent.created*', 'eloquent.updated*'],
],
],
];
// app/Providers/TelescopeServiceProvider.php
class TelescopeServiceProvider extends ServiceProvider
{
public function register(): void
{
Telescope::night(); // Dark Mode
// Slow Query হলে Alert দিন
Telescope::filter(function (IncomingEntry $entry) {
if ($entry->type === EntryType::QUERY) {
return $entry->content['slow'] ?? false;
}
return true;
});
}
}
**Custom Profiler — Detailed Performance Tracking:**
shell
php
// app/Services/PerformanceProfiler.php
class PerformanceProfiler
{
private array $timings = [];
private array $memories = [];
private float $startTime;
private int $startMemory;
public function start(string $label): void
{
$this->timings[$label] = microtime(true);
$this->memories[$label] = memory_get_usage(true);
}
public function end(string $label): array
{
$duration = (microtime(true) - $this->timings[$label]) * 1000;
$memoryUsed = memory_get_usage(true) - $this->memories[$label];
$result = [
'label' => $label,
'duration_ms' => round($duration, 2),
'memory_kb' => round($memoryUsed / 1024, 2),
'slow' => $duration > 100, // ১০০ms এর বেশি = Slow
];
// Slow Operation Log করুন
if ($result['slow']) {
Log::warning("Slow Operation Detected", $result);
}
return $result;
}
public function measure(string $label, callable $callback): mixed
{
$this->start($label);
$result = $callback();
$timing = $this->end($label);
return $result;
}
}
// ব্যবহার
class DashboardController extends Controller
{
public function index(PerformanceProfiler $profiler): JsonResponse
{
$profiler->start('total_dashboard');
// Products Load
$products = $profiler->measure('load_products', function () {
return Product::with(['category', 'brand'])
->where('status', 'active')
->take(20)
->get();
});
// Sales Report
$salesData = $profiler->measure('load_sales', function () {
return Order::selectRaw('DATE(created_at) as date, SUM(total) as revenue')
->whereBetween('created_at', [
now()->subDays(30),
now()
])
->groupBy('date')
->get();
});
// User Stats
$userStats = $profiler->measure('load_user_stats', function () {
return [
'total' => User::count(),
'active' => User::where('last_login_at', '>', now()->subDays(7))->count(),
'new' => User::whereDate('created_at', today())->count(),
];
});
$totalTime = $profiler->end('total_dashboard');
// Production এ Log করুন
if (app()->environment('production')) {
Log::info('Dashboard Performance', [
'total_ms' => $totalTime['duration_ms'],
'user_id' => auth()->id(),
'request_id' => request()->header('X-Request-ID'),
]);
}
return response()->json([
'products' => $products,
'sales' => $salesData,
'user_stats' => $userStats,
'_debug' => app()->environment('local') ? $totalTime : null,
]);
}
}
**Query Profiling — Slow Query খুঁজুন:**
php
php
// app/Providers/AppServiceProvider.php
public function boot(): void
{
if (config('app.debug')) {
// সব Query Log করুন
DB::listen(function (QueryExecuted $query) {
$duration = $query->time; // milliseconds
// ১০০ms এর বেশি হলে Log করুন
if ($duration > 100) {
Log::warning('Slow Query Detected', [
'sql' => $query->sql,
'bindings' => $query->bindings,
'duration' => "{$duration}ms",
'connection' => $query->connectionName,
'trace' => collect(debug_backtrace())
->take(5)
->pluck('file')
->toArray(),
]);
}
});
}
}
// Query Analysis — EXPLAIN দিয়ে
class QueryAnalyzer
{
public function explain(string $sql, array $bindings = []): array
{
// MySQL EXPLAIN — Query Plan দেখুন
$explained = DB::select("EXPLAIN " . $sql, $bindings);
$issues = [];
foreach ($explained as $row) {
// Full Table Scan — Index নেই!
if ($row->type === 'ALL') {
$issues[] = [
'type' => 'full_table_scan',
'table' => $row->table,
'rows' => $row->rows,
'message' => "⚠️ {$row->table} এ Full Table Scan!
Index যোগ করুন।",
];
}
// No Index Used
if (empty($row->key)) {
$issues[] = [
'type' => 'no_index',
'table' => $row->table,
'message' => "⚠️ {$row->table} এ কোনো Index ব্যবহার
হচ্ছে না।",
];
}
}
return [
'plan' => $explained,
'issues' => $issues,
];
}
}
---
🚀 **PART 2 — Laravel Specific Optimizations**
**Database Optimization:**
php
php
// ─────────────────────────────────────
// ১. N+1 Query Problem — সবচেয়ে Common
// ─────────────────────────────────────
// ❌ ভুল — ১০১টা Query!
$orders = Order::all(); // 1 Query
foreach ($orders as $order) {
echo $order->user->name; // +1 Query প্রতিটায়
echo $order->product->name; // +1 Query প্রতিটায়
}
// ১০০ Orders = ২০১ Queries!
// ✅ সঠিক — মাত্র ৩টা Query
$orders = Order::with(['user', 'product', 'payment'])
->get();
// ✅ আরো ভালো — দরকারী Field শুধু নিন
$orders = Order::with([
'user:id,name,email', // শুধু এই Fields
'product:id,name,price', // শুধু এই Fields
])
->select(['id', 'user_id', 'product_id', 'total', 'status'])
->get();
// ─────────────────────────────────────
// ২. Select Only Required Columns
// ─────────────────────────────────────
// ❌ ভুল — সব Column নিচ্ছে (SELECT *)
$users = User::all();
// ✅ সঠিক — দরকারী Column শুধু
$users = User::select(['id', 'name', 'email', 'created_at'])->get();
// ─────────────────────────────────────
// ৩. Index Optimization
// ─────────────────────────────────────
class AddPerformanceIndexes extends Migration
{
public function up(): void
{
// Single Column Index
Schema::table('orders', function (Blueprint $table) {
$table->index('status');
$table->index('user_id');
$table->index('created_at');
});
// Composite Index — একসাথে filter করলে
Schema::table('orders', function (Blueprint $table) {
// WHERE user_id = ? AND status = ?
$table->index(['user_id', 'status']);
// WHERE status = ? ORDER BY created_at DESC
$table->index(['status', 'created_at']);
});
// Covering Index — Index থেকেই সব Data পাবে, Table যেতে হবে না
Schema::table('products', function (Blueprint $table) {
// SELECT id, name, price WHERE category_id = ? AND status = ?
$table->index(['category_id', 'status', 'id', 'name', 'price']);
});
// Full-text Index — Search এর জন্য
Schema::table('products', function (Blueprint $table) {
$table->fullText(['name', 'description']);
});
}
}
// ─────────────────────────────────────
// ৪. Query Chunking — Memory Optimize
// ─────────────────────────────────────
// ❌ ভুল — লক্ষ Row Memory তে Load
$products = Product::all(); // Out of Memory!
// ✅ সঠিক — Chunk করে Process
Product::chunkById(1000, function (Collection $products) {
foreach ($products as $product) {
// Process করুন
$this->processProduct($product);
}
});
// ✅ আরো ভালো — Lazy Collection
Product::lazy(500)->each(function (Product $product) {
$this->processProduct($product);
// Memory: শুধু ৫০০টা একসাথে
});
// ─────────────────────────────────────
// ৫. Aggregate Query Optimization
// ─────────────────────────────────────
// ❌ ভুল — সব Record Load করে Count
$total = Order::where('status', 'completed')->get()->count();
// ✅ সঠিক — DB তেই Count করুন
$total = Order::where('status', 'completed')->count();
// ❌ ভুল — PHP তে Sum করুন
$revenue = Order::where('status', 'completed')
->get()
->sum('total');
// ✅ সঠিক — DB তে Sum করুন
$revenue = Order::where('status', 'completed')->sum('total');
// Multiple Aggregates একসাথে
$stats = Order::where('status', 'completed')
->selectRaw('
COUNT(*) as total_orders,
SUM(total) as total_revenue,
AVG(total) as avg_order_value,
MAX(total) as highest_order,
MIN(total) as lowest_order
')
->first();
**Laravel Cache Optimization:**
php
php
// app/Services/OptimizedProductService.php
class OptimizedProductService
{
// ─────────────────────────────────────
// Multi-level Cache
// ─────────────────────────────────────
private array $localCache = []; // Request-level Cache
public function getProduct(int $id): ?Product
{
// Level 1: Local Array Cache (সবচেয়ে দ্রুত)
if (isset($this->localCache["product:{$id}"])) {
return $this->localCache["product:{$id}"];
}
// Level 2: Redis Cache
$product = Cache::remember(
"product:{$id}",
now()->addHours(6),
// Level 3: Database
fn() => Product::with(['category:id,name', 'brand:id,name'])
->find($id)
);
// Local Cache এ রাখুন
$this->localCache["product:{$id}"] = $product;
return $product;
}
// ─────────────────────────────────────
// Batch Cache Loading
// ─────────────────────────────────────
public function getProducts(array $ids): Collection
{
// Cache থেকে যা আছে নিন
$cached = [];
$missing = [];
foreach ($ids as $id) {
$product = Cache::get("product:{$id}");
if ($product) {
$cached[$id] = $product;
} else {
$missing[] = $id;
}
}
// শুধু Missing গুলো DB থেকে নিন
if (!empty($missing)) {
$products = Product::with(['category', 'brand'])
->whereIn('id', $missing)
->get()
->keyBy('id');
// Cache এ রাখুন — Pipeline দিয়ে (দ্রুত)
Redis::pipeline(function ($pipe) use ($products) {
foreach ($products as $product) {
$pipe->setex(
"product:{$product->id}",
3600 * 6,
serialize($product)
);
}
});
$cached = array_merge($cached, $products->toArray());
}
return collect($cached);
}
// ─────────────────────────────────────
// Computed Properties Cache
// ─────────────────────────────────────
public function getProductStats(int $productId): array
{
return Cache::remember(
"product:{$productId}:stats",
now()->addMinutes(30),
function () use ($productId) {
return [
'total_sold' => OrderItem::where('product_id', $productId)
->sum('quantity'),
'revenue' => OrderItem::where('product_id', $productId)
->sum(DB::raw('quantity * price')),
'avg_rating' => Review::where('product_id', $productId)
->avg('rating'),
'review_count' => Review::where('product_id', $productId)
->count(),
'view_count' => (int) Redis::get("product:{$productId}:views"),
];
}
);
}
}
**PHP OpCode Cache & Configuration:**
php
ini
; php.ini / docker/php/php.ini
; ─────────────────────────────────────
; OPcache — PHP Code Cache
; ─────────────────────────────────────
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256 ; 256MB
opcache.interned_strings_buffer=16 ; 16MB
opcache.max_accelerated_files=20000 ; File Count
opcache.revalidate_freq=0 ; Production: 0 (Never Recheck)
opcache.validate_timestamps=0 ; Production: 0
opcache.save_comments=1
opcache.fast_shutdown=1
; ─────────────────────────────────────
; PHP Configuration
; ─────────────────────────────────────
memory_limit=512M
max_execution_time=60
max_input_time=60
upload_max_filesize=20M
post_max_size=20M
; Realpath Cache
realpath_cache_size=4096K
realpath_cache_ttl=600
php
php
// Laravel Production Optimization Commands
// Deploy এর সময় চালান:
// php artisan optimize — সব একসাথে
// অথবা আলাদা আলাদা:
php artisan config:cache // Config Cache করুন
php artisan route:cache // Route Cache করুন
php artisan view:cache // Blade View Cache করুন
php artisan event:cache // Event Cache করুন
// app/Console/Commands/OptimizeForProduction.php
class OptimizeForProduction extends Command
{
protected $signature = 'app:optimize-production';
protected $description = 'Production Optimization চালান';
public function handle(): void
{
$this->info('🚀 Optimization শুরু হচ্ছে...');
$steps = [
['php artisan config:cache', 'Config Cache'],
['php artisan route:cache', 'Route Cache'],
['php artisan view:cache', 'View Cache'],
['php artisan event:cache', 'Event Cache'],
['php artisan icons:cache', 'Icons Cache'],
];
foreach ($steps as [$command, $label]) {
$this->call(trim(str_replace('php artisan ', '', $command)));
$this->line(" ✅ {$label} সম্পন্ন");
}
// Cache Warm করুন
$this->call('cache:warm');
$this->line(" ✅ Cache Warm সম্পন্ন");
$this->info('✨ Optimization সম্পন্ন!');
}
}
---
⚖️ **PART 3 — Load Balancing**
**Load Balancing কী?**
php
Load Balancing = Traffic কে একাধিক Server এ ভাগ করা
একটা Server:
[১০০০ Requests] → [Server 1] ← Overwhelmed! 😰
Load Balanced:
[১০০০ Requests] → [Load Balancer]
├── [Server 1] ← ৩৩৩ Requests
├── [Server 2] ← ৩৩৩ Requests
└── [Server 3] ← ৩৩৪ Requests
Load Balancing Algorithms:
├── Round Robin → পালাক্রমে (১→২→৩→১→২→৩)
├── Least Connection → যার Connection কম তাকে দাও
├── IP Hash → Same User → Same Server
├── Weighted → শক্তিশালী Server বেশি পাবে
└── Random → Random Server এ পাঠাও
**Nginx Load Balancer Configuration:**
php
nginx
/etc/nginx/nginx.conf
─────────────────────────────────────
Upstream — Backend Servers Define করুন
─────────────────────────────────────
upstream laravel_app {
# Load Balancing Algorithm
least_conn; # Least Connection — সবচেয়ে কম Busy Server
# Backend Servers
server 10.0.0.1:9000 weight=3; # শক্তিশালী — বেশি Traffic পাবে
server 10.0.0.2:9000 weight=2;
server 10.0.0.3:9000 weight=1;
# Health Check — Dead Server বাদ দিন
server 10.0.0.4:9000 backup; # Primary সব Down হলে Backup
# Keepalive Connections — Performance বাড়ায়
keepalive 32;
}
API Servers — আলাদা Upstream
upstream laravel_api {
ip_hash; # Same User → Same Server (Session Consistency)
server 10.0.1.1:9000;
server 10.0.1.2:9000;
server 10.0.1.3:9000;
# Server Down হলে Timeout
server 10.0.1.4:9000 max_fails=3 fail_timeout=30s;
}
─────────────────────────────────────
Rate Limiting Zones
─────────────────────────────────────
limit_req_zone $binary_remote_addr
zone=api_limit:10m # 10MB Memory
rate=100r/m; # প্রতি মিনিটে ১০০ Request
limit_req_zone $binary_remote_addr
zone=login_limit:10m
rate=5r/m; # Login: প্রতি মিনিটে ৫ Request
limit_conn_zone $binary_remote_addr
zone=conn_limit:10m; # Connection Limit
─────────────────────────────────────
Main Server Block
─────────────────────────────────────
server {
listen 443 ssl http2;
server_name myapp.com;
# SSL
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# ─────────────────────────
# Gzip Compression
# ─────────────────────────
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/javascript
application/json
application/javascript
application/xml
image/svg+xml;
# ─────────────────────────
# Static Files — CDN থেকে
# ─────────────────────────
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2|ttf)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header Vary "Accept-Encoding";
# CDN Origin — File না থাকলে S3 থেকে নিন
try_files $uri @cdn_origin;
}
location @cdn_origin {
proxy_pass https://myapp-assets.s3.amazonaws.com;
}
# ─────────────────────────
# API Routes
# ─────────────────────────
location /api/ {
# Rate Limiting
limit_req zone=api_limit burst=20 nodelay;
limit_conn conn_limit 10;
proxy_pass http://laravel_api;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Request-ID $request_id;
# Timeouts
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
# Buffer Settings
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
# Login Rate Limiting
location /api/auth/login {
limit_req zone=login_limit burst=3 nodelay;
proxy_pass http://laravel_api;
}
# ─────────────────────────
# Web Routes
# ─────────────────────────
location / {
proxy_pass http://laravel_app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# ─────────────────────────
# Health Check
# ─────────────────────────
location /health {
access_log off;
proxy_pass http://laravel_app;
}
# ─────────────────────────
# Monitoring
# ─────────────────────────
location /nginx-status {
stub_status on;
allow 10.0.0.0/8; # শুধু Internal IP
deny all;
}
}
**HAProxy — Advanced Load Balancing:**
ini
haproxy
/etc/haproxy/haproxy.cfg
global
maxconn 50000
log /dev/log local0
user haproxy
group haproxy
daemon
defaults
mode http
log global
option httplog
option dontlognull
option forwardfor
option http-server-close
timeout connect 5s
timeout client 30s
timeout server 30s
retries 3
─────────────────────────────────────
Frontend — User Facing
─────────────────────────────────────
frontend http_front
bind *:80
bind *:443 ssl crt /etc/ssl/myapp.pem
# HTTP → HTTPS Redirect
redirect scheme https if !{ ssl_fc }
# ACL — URL Pattern দিয়ে Route করুন
acl is_api path_beg /api/
acl is_ws path_beg /ws/ # WebSocket
# Different Backend এ পাঠান
use_backend api_backend if is_api
use_backend ws_backend if is_ws
default_backend web_backend
─────────────────────────────────────
Web Backend
─────────────────────────────────────
backend web_backend
balance leastconn
# Sticky Session — Same User → Same Server
cookie SERVERID insert indirect nocache
# Health Check
option httpchk GET /health HTTP/1.1\r\nHost:\ localhost
http-check expect status 200
server web1 10.0.0.1:80 check cookie s1 weight 100
server web2 10.0.0.2:80 check cookie s2 weight 100
server web3 10.0.0.3:80 check cookie s3 weight 100
server web4 10.0.0.4:80 check cookie s4 backup # Backup
─────────────────────────────────────
API Backend
─────────────────────────────────────
backend api_backend
balance roundrobin
option httpchk GET /health
# Slow Start — নতুন Server ধীরে ধীরে Traffic নেবে
server api1 10.0.1.1:80 check slowstart 30s
server api2 10.0.1.2:80 check slowstart 30s
server api3 10.0.1.3:80 check slowstart 30s
─────────────────────────────────────
Stats Dashboard
─────────────────────────────────────
listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 30s
stats auth admin:secret_password
📊 **PART 4 — Application Performance Monitoring (APM)**
php
php
// app/Http/Middleware/PerformanceMonitorMiddleware.php
class PerformanceMonitorMiddleware
{
public function handle(Request $request, Closure $next): Response
{
$startTime = microtime(true);
$startMemory = memory_get_usage(true);
$response = $next($request);
$duration = (microtime(true) - $startTime) * 1000;
$memoryUsed = (memory_get_usage(true) - $startMemory) / 1024 / 1024;
$queryCount = DB::getQueryLog() ? count(DB::getQueryLog()) : 0;
$peakMemory = memory_get_peak_usage(true) / 1024 / 1024;
// Response Header এ দিন — Debug এর জন্য
if (config('app.debug')) {
$response->headers->set('X-Response-Time', round($duration, 2) . 'ms');
$response->headers->set('X-Memory-Usage', round($memoryUsed, 2) . 'MB');
$response->headers->set('X-Query-Count', $queryCount);
$response->headers->set('X-Peak-Memory', round($peakMemory, 2) . 'MB');
}
// Metrics Collect করুন — Prometheus Format
$this->recordMetrics([
'route' => $request->route()?->getName() ?? 'unknown',
'method' => $request->method(),
'status' => $response->getStatusCode(),
'duration_ms' => round($duration, 2),
'memory_mb' => round($memoryUsed, 2),
'queries' => $queryCount,
]);
// Slow Request Alert
if ($duration > 2000) { // ২ সেকেন্ড
Log::warning('Slow Request Detected', [
'url' => $request->fullUrl(),
'method' => $request->method(),
'duration_ms' => round($duration, 2),
'queries' => $queryCount,
'user_id' => auth()->id(),
'ip' => $request->ip(),
]);
}
return $response;
}
private function recordMetrics(array $metrics): void
{
// Redis এ Metrics Store করুন
$key = "metrics:" . date('Y-m-d-H');
Redis::pipeline(function ($pipe) use ($key, $metrics) {
$pipe->hincrby($key, 'total_requests', 1);
$pipe->hincrbyfloat($key, 'total_duration', $metrics['duration_ms']);
$pipe->hincrby($key, "status_{$metrics['status']}", 1);
// Slow Request Count
if ($metrics['duration_ms'] > 1000) {
$pipe->hincrby($key, 'slow_requests', 1);
}
// Query Heavy Request
if ($metrics['queries'] > 20) {
$pipe->hincrby($key, 'heavy_query_requests', 1);
}
$pipe->expire($key, 86400 * 7); // ৭ দিন রাখুন
});
}
}
// Metrics Dashboard
class MetricsDashboardController extends Controller
{
public function index(): JsonResponse
{
$hours = [];
for ($i = 23; $i >= 0; $i--) {
$key = "metrics:" . now()->subHours($i)->format('Y-m-d-H');
$data = Redis::hgetall($key);
$totalRequests = (int) ($data['total_requests'] ?? 0);
$totalDuration = (float) ($data['total_duration'] ?? 0);
$hours[] = [
'hour' => now()->subHours($i)->format('H:00'),
'total_requests' => $totalRequests,
'avg_response_ms' => $totalRequests > 0
? round($totalDuration / $totalRequests, 2)
: 0,
'slow_requests' => (int) ($data['slow_requests'] ?? 0),
'status_200' => (int) ($data['status_200'] ?? 0),
'status_500' => (int) ($data['status_500'] ?? 0),
'error_rate' => $totalRequests > 0
? round(($data['status_500'] ?? 0) / $totalRequests * 100, 2)
: 0,
];
}
return response()->json([
'hourly_metrics' => $hours,
'summary' => $this->getSummary($hours),
]);
}
private function getSummary(array $hours): array
{
$totalRequests = array_sum(array_column($hours, 'total_requests'));
$avgResponse = collect($hours)
->where('total_requests', '>', 0)
->avg('avg_response_ms');
return [
'total_requests_24h' => $totalRequests,
'avg_response_ms' => round($avgResponse, 2),
'peak_hour' => collect($hours)
->sortByDesc('total_requests')
->first()['hour'],
];
}
}
---
🗄️ **PART 5 — Database Connection Pooling**
plaintext
php
// config/database.php — Connection Pool Configuration
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST'),
'database' => env('DB_DATABASE'),
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
// Connection Pool Settings
'options' => [
PDO::ATTR_PERSISTENT => true, // Persistent Connection
PDO::ATTR_EMULATE_PREPARES => false,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET time_zone='+06:00'", // Bangladesh
],
],
// PgBouncer — PostgreSQL Connection Pooler
// docker-compose.yml এ যোগ করুন:
//
// pgbouncer:
// image: edoburu/pgbouncer
// environment:
// DB_HOST: postgres
// DB_PORT: 5432
// MAX_CLIENT_CONN: 1000 # সর্বোচ্চ Client
// DEFAULT_POOL_SIZE: 20 # প্রতি DB ২০ Connection
// POOL_MODE: transaction # Transaction Level Pooling
// ports:
// - "5432:5432"
// app/Services/ConnectionPoolMonitor.php
class ConnectionPoolMonitor
{
public function getStats(): array
{
// Active Connections দেখুন
$connections = DB::select("
SELECT
count(*) as total,
sum(case when command = 'Sleep' then 1 else 0 end) as idle,
sum(case when command != 'Sleep' then 1 else 0 end) as active,
max(time) as max_query_time
FROM information_schema.processlist
WHERE user = ?
", [config('database.connections.mysql.username')]);
return [
'total_connections' => $connections[0]->total,
'idle_connections' => $connections[0]->idle,
'active_connections'=> $connections[0]->active,
'max_query_time_s' => $connections[0]->max_query_time,
'pool_usage_pct' => round(
$connections[0]->active / max($connections[0]->total, 1) * 100,
1
),
];
}
}
🔬 **PART 6 — Performance Testing**
nginx
php
// tests/Performance/ApiPerformanceTest.php
class ApiPerformanceTest extends TestCase
{
/** @test */
public function product_list_api_responds_within_200ms(): void
{
// Seed করুন
Product::factory()->count(100)->create();
$start = microtime(true);
$response = $this->getJson('/api/v1/products');
$duration = (microtime(true) - $start) * 1000;
$response->assertOk();
$this->assertLessThan(
200,
$duration,
"Product List API {$duration}ms নিয়েছে। ২০০ms এর মধ্যে হওয়া উচিত।"
);
}
/** @test */
public function no_n_plus_one_query_on_product_list(): void
{
Product::factory()->count(20)->create();
// Query Count Check করুন
DB::enableQueryLog();
$this->getJson('/api/v1/products');
$queryCount = count(DB::getQueryLog());
DB::disableQueryLog();
// ৫টার বেশি Query হওয়া উচিত না
$this->assertLessThanOrEqual(
5,
$queryCount,
"Product List এ {$queryCount} টা Query চললো। N+1 সমস্যা আছে!"
);
}
}
plaintext
bash
Apache Bench — Simple Load Test
ab -n 1000 -c 100 \
-H "Authorization: Bearer TOKEN" \
https://myapp.com/api/v1/products
Output:
Requests per second: 450 [#/sec]
Time per request: 222 ms
Failed requests: 0
k6 — Advanced Load Testing
k6/load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export let options = {
stages: [
{ duration: '2m', target: 100 }, // Ramp up
{ duration: '5m', target: 100 }, // Stay
{ duration: '2m', target: 200 }, // Spike
{ duration: '5m', target: 200 }, // Stay
{ duration: '2m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<500'], // ৯৫% Request ৫০০ms এর মধ্যে
http_req_failed: ['rate<0.01'], // ১% এর কম Error
},
};
export default function () {
let response = http.get('https://myapp.com/api/v1/products', {
headers: { Authorization: 'Bearer TOKEN' }
});
check(response, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
'has data': (r) => JSON.parse(r.body).data.length > 0,
});
sleep(1);
}
// চালান:
// k6 run k6/load-test.js
---
📋 **PART 7 — Performance Checklist**
php
php
// app/Console/Commands/PerformanceAudit.php
class PerformanceAudit extends Command
{
protected $signature = 'app:performance-audit';
protected $description = 'Performance সমস্যা খুঁজুন';
public function handle(): void
{
$this->info('🔍 Performance Audit শুরু হচ্ছে...');
$issues = [];
// ১. Cache Check
if (!config('cache.default') === 'redis') {
$issues[] = '⚠️ Redis Cache ব্যবহার করুন';
}
// ২. Queue Check
if (config('queue.default') === 'sync') {
$issues[] = '⚠️ Sync Queue Production এ ব্যবহার করবেন না';
}
// ৩. Debug Mode Check
if (config('app.debug')) {
$issues[] = '❌ APP_DEBUG=true Production এ বিপজ্জনক!';
}
// ৪. Slow Query Check
$slowQueries = DB::select("
SELECT query_time, sql_text
FROM mysql.slow_log
WHERE query_time > 1
ORDER BY query_time DESC
LIMIT 10
");
if (!empty($slowQueries)) {
$issues[] = '⚠️ ' . count($slowQueries) . 'টা Slow Query পাওয়া গেছে';
}
// ৫. Missing Index Check
$missingIndexes = DB::select("
SELECT table_name, column_name
FROM information_schema.columns
WHERE table_schema = ?
AND column_name LIKE '%_id'
AND column_name NOT IN (
SELECT column_name
FROM information_schema.statistics
WHERE table_schema = ?
)
", [config('database.connections.mysql.database'),
config('database.connections.mysql.database')]);
if (!empty($missingIndexes)) {
$issues[] = '⚠️ ' . count($missingIndexes) . 'টা Foreign Key এ Index নেই';
}
// Result
if (empty($issues)) {
$this->info('✅ কোনো সমস্যা পাওয়া যায়নি!');
} else {
$this->error('❌ ' . count($issues) . 'টা সমস্যা পাওয়া গেছে:');
foreach ($issues as $issue) {
$this->line(" {$issue}");
}
}
}
}
// চালান: php artisan app:performance-audit
---
📝 **সংক্ষেপে মনে রাখুন**
**Profiling আগে, Optimize পরে** — অনুমান না করে আগে Profile করুন, সমস্যা খুঁজুন, তারপর Fix করুন।
**Database** — N+1 Query সবচেয়ে Common সমস্যা। Eager Loading, Index, এবং Select Specific Columns ব্যবহার করুন।
**Cache** — Multi-level Cache ব্যবহার করুন। Local Array → Redis → Database এই ক্রমে।
**Load Balancing** — Nginx বা HAProxy দিয়ে Traffic ভাগ করুন। Least Connection Algorithm বেশিরভাগ ক্ষেত্রে ভালো।
**Testing** — Performance Test CI/CD এ যোগ করুন — যাতে Regression ধরা পড়ে।
**Laravel Specific** — `php artisan optimize` Deploy এ চালান, OPcache Enable করুন, Queue Worker আলাদা রাখুন।
Top comments (0)