INTEGRITY Документация

Создание распределённого хранилища конфигурации

Хранение конфигурационных данных приложения отлично подходит для Workers KV. Такие данные могут включать сведения для персонализации приложения под каждого пользователя или арендатора, включение функций для отдельных групп пользователей, ограничение доступа с помощью списков разрешений и списков запретов и так далее. Подобные сценарии часто связаны с большим объемом чтения, а Workers KV хорошо кеширует такие запросы, обеспечивая низкую задержку чтения для вашего приложения на Workers.

В этом примере данные конфигурации приложения используются для персонализации приложения Workers для каждого пользователя. Данные конфигурации хранятся во внешнем приложении и базе данных, а затем записываются в Workers KV через REST API.

Запись конфигурации из внешнего приложения в Workers KV

В некоторых случаях источник истины для данных конфигурации может находиться не в Workers KV. В этом случае используйте REST API Workers KV, чтобы записать данные конфигурации в пространство имён Workers KV.

Следующее внешнее приложение на Node.js показывает простой скрипт, который считывает данные пользователя из базы данных и записывает их в Workers KV с помощью библиотеки REST API.

index.js
const postgres = require('postgres');
const { Cloudflare } = require('cloudflare');
const { backOff } = require('exponential-backoff');

if(!process.env.DATABASE_CONNECTION_STRING || !process.env.CLOUDFLARE_EMAIL || !process.env.CLOUDFLARE_API_KEY || !process.env.CLOUDFLARE_WORKERS_KV_NAMESPACE_ID || !process.env.CLOUDFLARE_ACCOUNT_ID) {
console.error('Missing required environment variables.');
process.exit(1);
}

// Setup Postgres connection
const sql = postgres(process.env.DATABASE_CONNECTION_STRING);

// Setup Cloudflare REST API client
const client = new Cloudflare({
apiEmail: process.env.CLOUDFLARE_EMAIL,
apiKey: process.env.CLOUDFLARE_API_KEY,
});

// Function to sync Postgres data to Workers KV
async function syncPreviewStatus() {
console.log('Starting sync of user preview status...');

    try {
    	// Get all users and their preview status
    	const users = await sql`SELECT id, preview_features_enabled FROM users`;

    	console.log(users);

    	// Create the bulk update body
    	const bulkUpdateBody = users.map(user => ({
    		key: user.id,
    		value: JSON.stringify({
    			preview_features_enabled: user.preview_features_enabled
    		})
    	}));

    	const response = await backOff(async () => {
    		console.log("trying to update")
    		try{
    			const response = await client.kv.namespaces.bulkUpdate(process.env.CLOUDFLARE_WORKERS_KV_NAMESPACE_ID, {
    				account_id: process.env.CLOUDFLARE_ACCOUNT_ID,
    				body: bulkUpdateBody
    			});
    		}
    		catch(e){
    			// Implement your error handling and logging here
    			console.log(e);
    			throw e; // Rethrow the error to retry
    		}
    	});

    	console.log(`Sync complete. Updated ${users.length} users.`);
    } catch (error) {
    	console.error('Error syncing preview status:', error);
    }

}

// Run the sync function
syncPreviewStatus()
.catch(console.error)
.finally(() => process.exit(0));
.env
DATABASE_CONNECTION_STRING = <DB_CONNECTION_STRING_HERE>
CLOUDFLARE_EMAIL = <CLOUDFLARE_EMAIL_HERE>
CLOUDFLARE_API_KEY = <CLOUDFLARE_API_KEY_HERE>
CLOUDFLARE_ACCOUNT_ID = <CLOUDFLARE_ACCOUNT_ID_HERE>
CLOUDFLARE_WORKERS_KV_NAMESPACE_ID = <CLOUDFLARE_WORKERS_KV_NAMESPACE_ID_HERE>
db.sql
-- Create users table with preview_features_enabled flag
CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  username VARCHAR(100) NOT NULL,
  email VARCHAR(255) NOT NULL,
  preview_features_enabled BOOLEAN DEFAULT false
);

-- Insert sample users
INSERT INTO users (username, email, preview_features_enabled) VALUES
('alice', '[email protected]', true),
('bob', '[email protected]', false),
('charlie', '[email protected]', true);

В этом фрагменте кода приложение Node.js считывает данные пользователей из базы данных Postgres и с помощью библиотеки Cloudflare REST API для Node.js записывает эти данные в Workers KV, чтобы использовать их для конфигурации в нашем приложении Workers. Приложение также использует экспоненциальную задержку для повторных попыток в случае ошибок.

Использование конфигурационных данных из Workers KV в приложении Worker

Теперь конфигурационные данные хранятся в пространстве имён Workers KV, и мы можем использовать их в приложении Workers, чтобы персонализировать его для каждого пользователя.

index.ts
// Example configuration data stored in Workers KV:
// Key: "user-id-abc" | Value: {"preview_features_enabled": false}
// Key: "user-id-def" | Value: {"preview_features_enabled": true}

interface Env {
  USER_CONFIGURATION: KVNamespace;
}

export default {
  async fetch(request, env) {
    // Get user ID from query parameter
    const url = new URL(request.url);
    const userId = url.searchParams.get('userId');

    if (!userId) {
      return new Response('Please provide a userId query parameter', {
        status: 400,
        headers: { 'Content-Type': 'text/plain' }
      });
    }


		const userConfiguration = await env.USER_CONFIGURATION.get<{
			preview_features_enabled: boolean;
		}>(userId, {type: "json"});

		console.log(userConfiguration);

    // Build HTML response
    const html = `
      <!DOCTYPE html>
      <html>
        <head>
          <title>My App</title>
          <style>
            body {
              font-family: Arial, sans-serif;
              max-width: 800px;
              margin: 0 auto;
              padding: 20px;
            }
            .preview-banner {
              background-color: #ffeb3b;
              padding: 10px;
              text-align: center;
              margin-bottom: 20px;
              border-radius: 4px;
            }
          </style>
        </head>
        <body>
          ${userConfiguration?.preview_features_enabled ? `
            <div class="preview-banner">
              🎉 You have early access to preview features! 🎉
            </div>
          ` : ''}
          <h1>Welcome to My App</h1>
          <p>This is the regular content everyone sees.</p>
        </body>
      </html>
    `;

    return new Response(html, {
			headers: { "Content-Type": "text/html; charset=utf-8" }
    });
  }
} satisfies ExportedHandler<Env>;
{
	"$schema": "node_modules/wrangler/config-schema.json",
	"name": "<ENTER_WORKER_NAME>",
	"main": "src/index.ts",
	"compatibility_date": "2025-03-03",
	"observability": {
		"enabled": true
	},
	"kv_namespaces": [
		{
			"binding": "USER_CONFIGURATION",
			"id": "<YOUR_BINDING_ID>"
		}
	]
}

Этот код использует путь из URL и находит файл, связанный с этим путём, в хранилище KV. Он также задаёт в ответе подходящий MIME-тип, чтобы сообщить браузеру, как его обрабатывать. Чтобы получить значение из хранилища KV, этот код использует arrayBuffer для корректной работы с двоичными данными, такими как изображения, документы и видео- или аудиофайлы.

Оптимизация производительности конфигурации

Для повышения производительности можно объединять значения в меньшем количестве пар ключ-значение. Это позволяет повысить эффективность кеширования и снизить задержку.

Например, вместо того чтобы хранить конфигурацию каждого пользователя в отдельной паре «ключ-значение», можно хранить конфигурации всех пользователей в одной паре «ключ-значение». Такой подход подходит для случаев, когда данные конфигурации невелики и ими легко управлять в рамках одной пары «ключ-значение» (ограничение размера значения Workers KV составляет 25 MiB).