العودة إلى المدونة

Cloudflare Workers KV Instant: دليل تشغيلي لقراءة إعدادات اللعبة بزمن 1.62ms

نُشر في 2 أكتوبر 2026
Cloudflare Workers KV Instant: دليل تشغيلي لقراءة إعدادات اللعبة بزمن 1.62ms تم إنشاؤها بمساعدة الذكاء الاصطناعي

باختصار

اكتشف كيف يوفّر Cloudflare Workers KV Instant قراءات إعدادات بزمن 1.62ms p99 وتكرار كتابة 256ms، ويقضي على فشل الإعدادات القديمة في ألعابك تمامًا.

عندما يدفع فريق العمليات المباشرة (live-ops) تحديث إعدادات حرجًا — إصلاح ثغرة اقتصادية، أو تغيير طارئ في جدول الأحداث، أو تفعيل نافذة صيانة — لا ينبغي للاعبين على الجانب الآخر من الكوكب قراءة بيانات قديمة لمدة 4.38 ثانية. هذا هو زمن تكرار الكتابة عند المئين 99 (p99) في Cloudflare Workers KV الكلاسيكي. بالنسبة لصفحة تسويقية ثابتة، لا أحد يهتم. أما بالنسبة للعبة مباشرة حيث تكون أموال حقيقية أو نزاهة المنافسة على المحك، فإن فجوة الانتشار هذه تمثل مسؤولية.

أعلنت Cloudflare للتو عن Workers KV Instant، وهو وضع جديد لـ Workers KV يعمل بمتجر Quicksilver v2 الداخلي. نفس واجهة API المألوفة: get() وput() وlist() وdelete(). لكن المحرك مختلف تمامًا من الداخل — إنه نفس المحرك الذي يعالج عمليات البحث عن الإعدادات لكل طلب عبر الشبكة العالمية لـ Cloudflare. النتيجة: قراءات بزمن 1.62ms عند p99 وتكرار كتابة بزمن 256ms عند p99 إلى أكثر من 300 موقع حافة.

يغطي هذا الدليل التشغيلي ما الذي تغيّر فعليًا، وكيف تكتشف ما إذا كانت لعبتك تواجه أنماط فشل الإعدادات القديمة (stale-config)، وتنفيذًا خطوة بخطوة لإعدادات اللعبة المستضافة على الحافة، والحدود الصارمة التي تجعل KV Instant غير مناسب لبعض أعباء العمل، وأين يكون استخدام خدمة إعدادات عن بُعد مُدارة أكثر منطقية من بناء هذا بنفسك.


ما الذي تغيّر فعليًا: KV Instant مقابل KV Classic

إن Workers KV Classic متسق في النهاية (eventually consistent). تكتب مفتاحًا، وتقوم Cloudflare بتكراره إلى مواقع الحافة بشكل غير متزامن. خلال تلك النافذة، يمكن للقراء الحصول على قيم قديمة. نموذج الاتساق هو «آخر كتابة تفوز مع إبطال ذاكرة تخزين مؤقت يعتمد على TTL». هذا يعمل مع الأصول الثابتة وتفضيلات المستخدمين — البيانات التي تُكتب نادرًا ويمكنها تحمل ثوانٍ من القدم.

يستخدم KV Instant متجر Quicksilver v2، وهو المتجر الداخلي الذي بنته Cloudflare لتوزيع إعداداتها الخاصة. كل طلب إلى Cloudflare يمر بالفعل عبر Quicksilver — لقواعد التوجيه، وإعدادات جدار الحماية، وحدود معدل الطلبات. إنه مختبَر في أصعب الظروف على نطاق لن تقترب منه معظم الخوادم الخلفية للألعاب.

إليك المقارنة العملية للأداء:

المقياس KV Instant KV Classic
قراءات p99 (الكل) 1.62 ms 287 ms
تكرار كتابة p99 256 ms 4,380 ms
وسيط تكرار الكتابة 107 ms < 1 ثانية (بدون دقة دون الثانية)

هذا تحسين بمقدار 177 ضعفًا في زمن استجابة القراءة، وتحسين بمقدار 17 ضعفًا في تكرار الكتابة. هذه الأرقام مأخوذة من اختبارات Cloudflare الخاصة عبر جميع مواقع الحافة البالغ عددها أكثر من 300.

بالنسبة للخادم الخلفي للعبة، الآثار مباشرة: يمكنك قراءة الإعدادات في كل طلب لاعب — عند تسجيل الدخول، وعند بدء المباراة، وعند جلب المخزون، وعند فتح المتجر — وتبلغ تكلفة القراءة أقل من 2ms حتى عند p99. لا يوجد TTL تنتظره. ولا نافذة اتساق للكاش يرى فيها اللاعب (أ) إصلاح الثغرة بينما لا يراه اللاعب (ب).


دليل تشغيلي: اكتشاف فشل الإعدادات القديمة في لعبتك

قبل أن ترحّل أي شيء، تحتاج إلى معرفة ما إذا كنت تواجه هذه المشكلة. إليك أنماط الفشل، وكيفية اكتشافها، وما تكلفك.

نمط الفشل 1: قراءات قديمة منتهية TTL

ما الذي يتعطل: يستخدم مخزن الإعدادات لديك تخزينًا مؤقتًا قائمًا على TTL. يتم تحديث علم ميزة (feature flag)، لكن اللاعبين في طوكيو ما زالوا يقرؤون القيمة القديمة لمدة 30–60 ثانية حتى تنتهي صلاحية كاش الحافة.

كيف تكتشفه:

  • سجّل تجزئة إصدار الإعدادات (config version hash) المرسلة إلى كل عميل مع الطابع الزمني للكتابة عند آخر تحديث للإعدادات.
  • استعلم عن العملاء الذين يستلمون إصدار إعدادات أقدم من ثانيتين بعد الكتابة.
  • أنشئ لوحة معلومات: count of (stale_reads) / count (total_reads). أي قيمة أعلى من 0% أثناء دفع الإعدادات تعني وجود نافذة قراءات قديمة.

نمط استعلام تشخيصي سريع (كيِّفه مع بنية التسجيل لديك):

SELECT
  received_config_version,
  expected_config_version,
  COUNT(*) AS stale_count,
  MAX(received_at - config_updated_at) AS max_staleness
FROM config_read_log
WHERE config_updated_at > NOW() - INTERVAL '1 hour'
  AND received_config_version != expected_config_version
GROUP BY received_config_version, expected_config_version
ORDER BY max_staleness DESC;

ما تكلفك: اللاعبون في نافذة القدم يعيشون حالات لعب مختلفة. في لعبة تنافسية، يرى أحد اللاعبين أن الثغرة أُصلحت بينما لا يراها الآخر. في لعبة تعتمد على الأحداث، يفوت بعض اللاعبين النوافذ محدودة الوقت تمامًا. هذه مشكلة ثقة.

نمط الفشل 2: قراءات الإعدادات في المسار الحرج تسبب قفزات في زمن الاستجابة

ما الذي يتعطل: زمن استجابة القراءة لمخزن الإعدادات لديك مرتفع بما يكفي (100–300ms) بحيث لا يمكنك تحمل قراءته في كل طلب. بدلًا من ذلك، تخزنه مؤقتًا في جهة العميل أو في كاش محلي سريع لكنه قديم. يكون الكاش صحيحًا 99% من الوقت، لكن عندما يكون خاطئًا، يكون خاطئًا جدًا.

كيف تكتشفه:

  • قِس زمن استجابة p50 وp95 وp99 لاستدعاء قراءة الإعدادات. إذا كان p99 أعلى من 50ms، فهو بطيء جدًا للفحوصات لكل طلب.
  • تتبّع معدلات إصابة الكاش. إذا كنت تخزّن الإعدادات مؤقتًا في جهة العميل لتجنب الوصول إلى المخزن، فأنت بالفعل قبلت القدم كتنازل.
  • راقب الحوادث التي تستمر فيها قيمة إعدادات خاطئة بعد الدفع — وتتبّعها حتى TTL الكاش لكل عميل.

ما تكلفك: أنت تهندس حول مشكلة زمن استجابة تؤدي إلى مشكلة قدم. مشكلتان بسعر مشكلة واحدة.

نمط الفشل 3: تضخم الكتابة تحت ضغط الحوادث

ما الذي يتعطل: تحتاج إلى دفع تحديث إعدادات طارئ — تعطيل ميزة، أو تفعيل وضع الصيانة، أو الإشارة إلى اقتصاد معطل — وتكون الكتابة بطيئة أو محدودة المعدل. يسمح Workers KV الكلاسيكي بكتابة واحدة لكل مفتاح في الثانية، ويستغرق التكرار ثوانٍ.

كيف تكتشفه:

  • تتبّع زمن الاستجابة من الكتابة حتى الظهور أثناء الاستجابة للحوادث. إذا كان فريق العمليات يعتمد على «يجب أن تنتشر الإعدادات في بضع ثوانٍ» ويستغرق الأمر 10 ثوانٍ أو أكثر، فإن مخزن الإعدادات لديك يمثل عنق زجاجة للحوادث.
  • راقب فشل الكتابة واستجابات حد المعدل 429 أثناء الدفعات عالية الاستعجال.

إذا كنت تواجه أنماط الفشل الثلاثة هذه، فإن KV Instant يستحق التقييم.


تنفيذ KV Instant لإعدادات اللعبة: خطوة بخطوة

إن KV Instant حاليًا في نسخة بيتا خاصة. يمكنك التسجيل عبر نموذج بيتا الخاص بـ Cloudflare. التنفيذ مباشر لأن واجهة API مطابقة لـ Workers KV الكلاسيكي — يختلف فقط إنشاء مساحة الاسم (namespace).

الخطوة 1: إنشاء مساحة اسم KV Instant

مرّر خاصية mode: "instant" عند إنشاء مساحة الاسم:

wrangler kv namespace create "GAME_CONFIG" --mode instant

يؤدي هذا إلى إنشاء ربط مساحة الاسم. حدّث ملف wrangler.toml:

[[kv_namespaces]]
binding = "GAME_CONFIG"
id = "&lt;your-namespace-id>"

الخطوة 2: كتابة إعدادات لعبتك

مساحات أسماء KV Instant محدودة بـ 10,000 زوج مفتاح-قيمة بحجم إجمالي 1 ميجابايت. يمكن أن يصل حجم كل مفتاح إلى 300 بايت. هذا صغير — عن قصد. إنه مصمم لأعلام الميزات والإعدادات، وليس لبيانات اللاعبين.

نظّم مفاتيحك لطبقة إعدادات لعبتك:

// In a Cloudflare Worker that manages config
async function updateGameConfig(env) {
  const config = {
    maintenanceMode: false,
    maintenanceMessage: "Servers are updating. Back in 5 min.",
    eventSchedule: {
      currentEvent: "summer_showdown_2025",
      startTime: "2025-07-15T18:00:00Z",
      endTime: "2025-07-22T18:00:00Z",
    },
    economyTuning: {
      xpMultiplier: 1.5,
      goldDropRate: 0.85,
      shopRefreshHours: 6,
    },
    featureFlags: {
      newMatchmaking: true,
      rankedModeV2: false,
      socialLobby: true,
    },
    buildVersion: {
      minimumClient: "1.4.2",
      forceUpdate: false,
    },
  };

  await env.GAME_CONFIG.put("active_config", JSON.stringify(config));
  // Propagates to 300+ edge locations in ~256ms at p99
}

حد تكرار الكتابة: كتابة واحدة لكل مساحة اسم في الثانية. هذا قيد تصميمي، وليس خطأ برمجيًا — فهو يجعل ترتيب التحديثات حتميًا. بالنسبة لإعدادات تتغير بضع مرات في الساعة (أو أثناء الحادث)، لا يمثل هذا عنق زجاجة.

الخطوة 3: تقديم الإعدادات من الحافة

أنشئ Worker في Cloudflare يقرأ الإعدادات في كل طلب ويقدمها إلى عميل لعبتك:

export default {
  async fetch(request, env, ctx) {
    // Every player request reads fresh config — 1.62ms p99
    const raw = await env.GAME_CONFIG.get("active_config");
    if (!raw) {
      return new Response(JSON.stringify({ error: "config_missing" }), {
        status: 503,
        headers: { "Content-Type": "application/json" },
      });
    }

    const config = JSON.parse(raw);

    // Conditional logic at the edge — maintenance mode check
    if (config.maintenanceMode) {
      return new Response(
        JSON.stringify({
          status: "maintenance",
          message: config.maintenanceMessage,
        }),
        {
          status: 503,
          headers: { "Content-Type": "application/json" },
        }
      );
    }

    // Return relevant config slice for the client
    const clientConfig = {
      event: config.eventSchedule,
      economy: config.economyTuning,
      features: config.featureFlags,
      build: config.buildVersion,
    };

    return new Response(JSON.stringify(clientConfig), {
      headers: {
        "Content-Type": "application/json",
        "Cache-Control": "public, max-age=5", // Short cache for freshness
      },
    });
  },
};

الخطوة 4: استهلاك الإعدادات في عميل لعبتك

في جهة العميل، اجلب الإعدادات أثناء التهيئة أو بداية الجلسة. إليك مثال بلغة GDScript للعبة Godot:

extends Node

var config_url: String = "https://config.yourgame.com/api/config"
var current_config: Dictionary = {}

func _ready():
    fetch_config()

func fetch_config():
    var http = HTTPRequest.new()
    add_child(http)
    http.request_completed.connect(_on_config_received)
    http.request(config_url)

func _on_config_received(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray):
    if response_code != 200:
        push_warning("Config fetch failed: %d" % response_code)
        return

    var json = JSON.new()
    var parse_result = json.parse(body.get_string_from_utf8())
    if parse_result != OK:
        push_warning("Config parse error")
        return

    current_config = json.data
    _apply_config(current_config)

func _apply_config(config: Dictionary):
    # Apply feature flags
    if config.has("features"):
        if config["features"].get("rankedModeV2", false):
            enable_ranked_mode()
        if config["features"].get("newMatchmaking", false):
            enable_new_matchmaking()

    # Check build version
    if config.has("build"):
        var min_version = config["build"].get("minimumClient", "0.0.0")
        if version_compare(get_app_version(), min_version) &lt; 0 and config["build"].get("forceUpdate", false):
            show_force_update_screen()

    # Apply economy tuning
    if config.has("economy"):
        EconomyManager.set_xp_multiplier(config["economy"].get("xpMultiplier", 1.0))
        EconomyManager.set_gold_drop_rate(config["economy"].get("goldDropRate", 1.0))

    print("Config applied successfully — all players now on same state")

نظرًا لأن القراءات أقل من 2ms ولا يوجد قدم بسبب TTL، يمكنك استدعاء fetch_config() عند كل بداية جلسة، أو عند كل دخول إلى طابور المباراة، أو عند كل إجراء عميل مهم دون القلق بشأن عبء زمن الاستجابة أو اتساق الكاش.


ما لا يمكن أن يفعله KV Instant (حدود صارمة)

إن KV Instant قوي في مجاله، لكن القيود حقيقية. قيّم هذه قبل أن تلتزم بالتنفيذ:

حجم مساحة الاسم الإجمالي 1 ميجابايت. لا يمكنك تخزين بيانات اللاعبين، أو لقطات لوحات الصدارة، أو المخزون، أو أي شيء ينمو مع عدد لاعبيك. هذا مخصص فقط للإعدادات والأعلام المطبقة عالميًا.

الحد الأقصى 10,000 زوج مفتاح-قيمة. يكفي لمئات أعلام الميزات وكائنات الإعدادات. لكنه غير كافٍ لأي شيء لكل لاعب.

كتابة واحدة لكل مساحة اسم في الثانية. إذا كنت بحاجة إلى تكرار كتابة أقل من ثانية، فهذا ليس مخزنك. بالنسبة لإعدادات اللعبة التي تتغير كل ساعة أو أثناء الحوادث، فهذا جيد. لمزامنة حالة اللعبة في الوقت الفعلي، ابحث في مكان آخر.

لا دعم للبيانات الوصفية. يُرجع getWithMetadata قيمة null. لا يمكنك إرفاق بيانات وصفية مخصصة بالمفاتيح. إذا كنت تعتمد على البيانات الوصفية للإصدار أو الوسوم، فستحتاج إلى تضمين ذلك في القيمة نفسها.

لا ترقيم في list. كل استدعاء list يُرجع جميع المفاتيح المطابقة في مساحة الاسم. بالنسبة إلى 10,000 مفتاح، فهذه استجابة كبيرة. كن مقصودًا في تسمية المفاتيح والبادئات لتحديد نطاق استدعاءات list.

تباين التكلفة. التخزين بتكلفة 100 دولار/ميجابايت/شهريًا (مقابل 0.50 دولار/جيجابايت/شهريًا للكلاسيكي). عمليات الكتابة من الفئة A تكلف 0.10 دولار لكل عملية (مقابل 5.00 دولارات لكل مليون للكلاسيكي). هذه الأرقام باهظة لأعباء العمل عالية الكتابة. لكن القراءات تكلف 0.20 دولار لكل مليون — أرخص بنسبة 60% من الكلاسيكي. نموذج التسعير منحرف بشدة نحو «اكتب نادرًا، اقرأ باستمرار»، وهو بالضبط نمط إعدادات اللعبة.


أفضل الممارسات لإعدادات اللعبة على KV Instant

  1. سمِّ مفاتيحك بقصد. استخدم بادئات مثل ff_ لأعلام الميزات، وecon_ لضبط الاقتصاد، وevt_ لجداول الأحداث. هذا يجعل استدعاءات list قابلة للفحص، ويتيح لك بناء واجهات إدارة إعدادات تتعامل مع فئات محددة دون قراءة مساحة الاسم بالكامل.

  2. ضمّن تجزئات الإصدار في قيمك. نظرًا لعدم دعم البيانات الوصفية، أضف حقل configVersion داخل كل قيمة إعدادات. يمكن لعميلك الإبلاغ عن هذا الإصدار في السجلات والتحليلات، مما يمنحك لوحة تحقق من الانتشار في الوقت الفعلي.

  3. افصل الإعدادات عن الحالة. يخزن KV Instant الإعدادات — القواعد، والأعلام، ومقابض الضبط، والجداول. لا يخزن الحالة — مخزون اللاعبين، ونتائج المباريات، وترتيبات لوحات الصدارة. صمّم هذين كنظامين منفصلين بقواعد تخزين منفصلة. إذا كانت مساحة اسم «الإعدادات» لديك تنمو بأكثر من بضعة كيلوبايتات أسبوعيًا، فضع تلك البيانات في مكان آخر.

  4. تعامل مع حالة 503. إذا أرجعت GAME_CONFIG.get() قيمة null، يجب أن يفشل الـ Worker الخاص بك بأمان. أعد وضع الصيانة أو مفتاح إيقاف. لا ينبغي أبدًا أن يتعطل عميل لعبتك بسبب مفتاح إعدادات مفقود. ابنِ آلية التراجع في Worker الحافة، وليس في العميل.

  5. اختبر ترتيب الكتابة تحت الضغط. كتابة واحدة في الثانية لكل مساحة اسم تعني أن الكتابات المتزامنة ستتم تسلسليًا. إذا دفع مهندسان تغييرات إعدادات في نفس الثانية، يفوز آخر كتابة. أنشئ قائمة انتظار لتغييرات الإعدادات بترتيب صريح بدلًا من الاعتماد على الكتابات المتزامنة.


متى تستخدم خدمة إعدادات مُدارة بدلًا من ذلك

بناء خط أنابيب توزيع إعدادات على KV Instant هو قرار هندسي سليم إذا كان فريقك يملك القدرة على تحمل مسؤولية Worker الحافة، وواجهة إدارة الإعدادات، ونظام الإصدارات، ومنطق الجلب في جهة العميل، وإجراءات الاستجابة للحوادث المحيطة بذلك. هذا عمل بنية تحتية حقيقي — ليس صعبًا بشكل فردي، لكنه يتراكم عبر الجدول الزمني لإصدارك.

للفرق التي تريد توزيع الإعدادات دون تشغيل البنية التحتية، توفر horizOn الإعدادات عن بُعد (Remote Configuration) كخدمة مُدارة. يمكنك تحديد أعلام الميزات وإعدادات اللعبة ومعلمات الضبط عبر لوحة التحكم أو API، وتتولى المنصة توزيعها على عملاء لعبتك. لا حاجة لكتابة أو صيانة Workers للحافة، ولا قيود على حجم مساحة اسم KV للتفكير فيها — لكن أيضًا تحكم أقل في محرك التكرار الأساسي وخصائص زمن الاستجابة.

المفاضلة هي المحور الكلاسيكي بين البناء والشراء. يمنحك KV Instant أداءً خامًا على الحافة مع تحكم كامل. بينما تمنحك الخدمة المُدارة وقت تكامل أسرع ومساحة تشغيل أقل. اختر بناءً على ما إذا كان توزيع الإعدادات اختصاصًا أساسيًا لاستوديوهاتك أم عبء بنية تحتية.

إذا كنت بحاجة إلى اكتشاف قدم الإعدادات عبر عملاء اللعبة الموزعين، فإن أنماط التسجيل والاستعلام في قسم الدليل التشغيلي أعلاه تعمل بغض النظر عن أي خادم إعدادات خلفي تختاره. طبقة التشخيص مستقلة عن وسيلة النقل.


الخلاصة: قراءات إعدادات تواكب لعبتك

يمثل KV Instant ترقية مهمة للنمط المحدد لـ«بيانات إعدادات صغيرة وحرجة تُقرأ عالميًا». بالنسبة للخوادم الخلفية للألعاب، يترجم هذا النمط مباشرة إلى أعلام الميزات، وضبط الاقتصاد، وجداول الأحداث، ومفاتيح الصيانة، وبوابات إصدارات البناء.

الأرقام ليست تقريبًا تسويقيًا: قراءات بزمن 1.62ms عند p99 وتكرار كتابة بزمن 256ms عند p99 عبر أكثر من 300 موقع حافة. واجهة API مطابقة لـ Workers KV الكلاسيكي. القيود (1 ميجابايت، 10,000 مفتاح، كتابة واحدة/ثانية) محددة بوضوح ومناسبة لحالة الاستخدام.

إذا كانت لعبتك تقرأ حاليًا الإعدادات من مخزن مركزي وتخزنها مؤقتًا في جهة العميل لتجنب زمن الاستجابة، فقيّم ما إذا كانت نافذة القدم هذه ما تزال مقبولة. بالنسبة للألعاب التنافسية وعناوين الخدمة المباشرة مع ضبط اقتصادي في الوقت الفعلي، فإن الإجابة بشكل متزايد هي لا.

سجّل في النسخة التجريبية الخاصة لـ KV Instant، وأنشئ مساحة اسم اختبارية مع أكثر إعداداتك حساسية لزمن الاستجابة، وقِس هامش الانتشار الذي تكسبه. إذا تطابقت الأرقام مع ما تعلنه Cloudflare، فلديك مسار واضح للقضاء تمامًا على فئة واحدة من حوادث الإعدادات القديمة.

هل تحتاج إلى رأي ثانٍ في بنية الإعدادات أو طوبولوجيا الخادم الخلفي؟ اطّلع على وثائق horizOn — إذ تتولى المنصة توزيع الإعدادات، والإبلاغ عن الأعطال، وإدارة جلسات اللاعبين لتتمكن من التركيز على إصدار أسلوب اللعب بدلًا من أدلة البنية التحتية التشغيلية.


المصدر: إطلاق Workers KV Instant — مدعوم من Quicksilver