Tối ưu hệ thống để xử lý dữ liệu với workers và D1 database

Tối ưu hệ thống để xử lý dữ liệu với workers và D1 database

Hệ thống của mình tối ưu với các bước workflows : nhập thông tin xem view từ web laravel lên D1 database và frontend sẽ lấy dữ liệu đó từ workers  mục đích giảm truy vấn đến backend  . đối với web ít traffic thì việc này đơn giản nếu nhiều thì nó rất đáng kể . Code workers của mình :

Đầu tiên về workers : src\Service\CacheService.js

const CacheService = {
	async get(cacheKey) {
		const cache = caches.default;
		const request = new Request(`https://cache.local/${cacheKey}`);
		const response = await cache.match(request);
		if (!response) return null;
		return await response.json();
	},
	// Mặc định là 5 phút (300 giây)
	async set(cacheKey, data, maxAge = 300) {
		const request = new Request(`https://cache.local/${cacheKey}`);
		const response = new Response(JSON.stringify(data), {
			status: 200,
			headers: {
				"Content-Type": "application/json",
				"Cache-Control": `s-maxage=${maxAge}`
			}
		});
		const cache = caches.default;
		await cache.put(request, response.clone());
		return response;
	},
	async delete(cacheKey) {
		const cache = caches.default;
		const request = new Request(`https://cache.local/${cacheKey}`);
		return await cache.delete(request);
	}
}
export default CacheService;

Tiếp theo đến file : src\index.js

import CacheService from './Service/CacheService';

function getCorsHeaders(request) {
	return {
		"Access-Control-Allow-Origin": "*",
		"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
		"Access-Control-Allow-Headers": "Content-Type, Authorization", // Thêm header cụ thể hơn nếu cần
	};
}

export default {
	async fetch(request, env, ctx) {
		const url = new URL(request.url);
		const pathnames = url.pathname.split('/').filter(Boolean); // Tách phần đường dẫn thành mảng không có phần tử rỗng
		const method = request.method;
		const key = url.searchParams.get('key'); // Lấy giá trị 'key' từ query string

		// Kiểm tra nếu key không hợp lệ (nếu cần)
		// if (key !== env.KEY) {
		// 	return new Response('Yêu cầu cần key ', { status: 400 });
		// }

		// Xử lý yêu cầu có 'api' trong đường dẫn
		if (pathnames[0] === 'post') {
			const post = pathnames[1] || null; // Lấy phần thứ hai trong đường dẫn là 'post'
			if (!post) {
				return new Response('yeu cau khong hop le', { status: 400 });
			}
			const cacheKey = `views_post_${post}`; // Tạo khóa cache từ post ID
			const cacheData = await CacheService.get(cacheKey); // Kiểm tra cache

			// Nếu dữ liệu đã có trong cache
			if (cacheData) {
				return new Response(JSON.stringify({ ...cacheData, cache: "hit" }), {
					status: 200,
					headers: {
						...getCorsHeaders(request),
						"Cache-Control": "public, max-age=50", // Cài đặt cache cho response
					}
				});
			}

			// Nếu không có trong cache, truy vấn cơ sở dữ liệu
			const result = await env.DB.prepare("SELECT count FROM views WHERE postid = ?").bind(post).first();

			// Dữ liệu trả về từ DB
			let responseData = {
				...result || { status: "error", message: "Error" },
				cache: "miss"
			};
			const statusCode = result ? 200 : 404;

			// Nếu có dữ liệu, lưu vào cache
			if (result) {
				responseData = {
					...responseData,
					status: "success",
					message: "OK"
				};
				await CacheService.set(cacheKey, responseData, 20); // Lưu vào cache với thời gian sống là 20 giây
			}

			return new Response(JSON.stringify(responseData), {
				status: statusCode,
				headers: {
					...getCorsHeaders(request),
					"Cache-Control": "public, max-age=20", // Cache response
				}
			});
		}

		// Trả về mặc định nếu không phải API
		return new Response(' /post/{postid} để lấy dữ liệu view của post ');
	},
};

Tiếp theo trong file : wrangler.jsonc

/**
 * For more details on how to configure Wrangler, refer to:
 * https://developers.cloudflare.com/workers/wrangler/configuration/
 */
{
	"$schema": "node_modules/wrangler/config-schema.json",
	"name": "nhatnhat",
	"main": "src/index.js",
	"compatibility_date": "2025-04-10",
	"observability": {
		"enabled": true
	},
	"d1_databases": [
		{
			"binding": "DB",
			"database_name": "xxxxxx",
			"database_id": "588d8a1a-xxxxxx"
		}
	],
	/**
	 * Smart Placement
	 * Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement
	 */
	// "placement": { "mode": "smart" },
	/**
	 * Bindings
	 * Bindings allow your Worker to interact with resources on the Cloudflare Developer Platform, including
	 * databases, object storage, AI inference, real-time communication and more.
	 * https://developers.cloudflare.com/workers/runtime-apis/bindings/
	 */
	/**
	 * Environment Variables
	 * https://developers.cloudflare.com/workers/wrangler/configuration/#environment-variables
	 */
	"vars": {
		"KEY": "hien123"
	},
	/**
	 * Note: Use secrets to store sensitive data.
	 * https://developers.cloudflare.com/workers/configuration/secrets/
	 */
	/**
	 * Static Assets
	 * https://developers.cloudflare.com/workers/static-assets/binding/
	 */
	// "assets": { "directory": "./public/", "binding": "ASSETS" },
	/**
	 * Service Bindings (communicate between multiple Workers)
	 * https://developers.cloudflare.com/workers/wrangler/configuration/#service-bindings
	 */
	// "services": [{ "binding": "MY_SERVICE", "service": "my-service" }]
}

Sau đó bạn chỉ cần deploy dữ liệu với lệnh : npm run deploy

2 Code trên laravel :

mình cài đặt laravel với lệnh :

composer create-project laravel/laravel la12connectd1cloudfare

Ở đây mình dùng gói 

composer require ntanduy/cloudflare-d1-database

Tiếp theo mình vào config :  config/database.php thêm dữ liệu database vào :

 'd1' => [
            'driver' => 'd1',
            'prefix' => '',
            'database' => env('CLOUDFLARE_D1_DATABASE_ID', ''),
            'api' => 'https://api.cloudflare.com/client/v4',
            'auth' => [
                'token' => env('CLOUDFLARE_TOKEN', ''),
                'account_id' => env('CLOUDFLARE_ACCOUNT_ID', ''),
            ],
        ],

thông tin env lấy ở đây :

CLOUDFLARE_D1_DATABASE_ID=xxx-89cc-44af-b0d1-bf03942e7b2b
CLOUDFLARE_TOKEN=xxxx
CLOUDFLARE_ACCOUNT_ID=xxx

giờ đến đoạn gửi dữ liệu lên D1 trong laravel : app\Jobs\IncrementJob.php

<?php

namespace App\Jobs;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class IncrementJob implements ShouldQueue
{
    use Queueable;

    public int $post;

    /**
     * Create a new job instance.
     */
    public function __construct($post)
    {
        $this->post = $post;
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        $increment = 1;

        // Kiểm tra bản ghi đã tồn tại hay chưa
        $check = DB::connection('d1')->table('views')->where('postid', $this->post)->first();

        if (!$check) {
            // Nếu không có bản ghi nào thì tạo mới
            DB::connection('d1')->table('views')->insert([
                'postid' => $this->post,
                'count' => 1, // Tạo count ban đầu là 1
            ]);
            // Bạn có thể log hoặc xử lý ở đây
            Log::info("Created new record for postid: " . $this->post);
        } else {
            // Nếu có bản ghi thì cập nhật
            DB::connection('d1')->table('views')->where('postid', $this->post)->update([
                'count' => DB::raw('IFNULL(count, 0) + 1'), // Tăng count lên 1
            ]);
            Log::info("Cập nhật view postid: " . $this->post);
        }
    }
}

Vậy là xong hehe