فیچر فلگها
ویژگیها را بدون استقرار مجدد روشن و خاموش کنید، برای درصدی از کاربران باز کنید یا برای گروهی هدف بگیرید. مقدار فلگها همراه پیکربندی به سرورها میرسد، پس خواندنشان تماس شبکه ندارد.
فلگ چیست#
هر فلگ به یک برنامه یا فانکشن تعلق دارد، یک کلید و یک نوع دارد: boolean، string، number یا json. برای هر محیط (پروداکشن، پیشنمایش، توسعه) تنظیمات جدا دارد، و هر شاخهٔ پیشنمایش میتواند تنظیمات خودش را داشته باشد.
تنظیمات یک محیط به ترتیب بررسی میشود: اگر فلگ قطع باشد مقدار «حالت قطع» (off_value) داده میشود؛ وگرنه اولین قانون هدفگیریِ مطابق؛ بعد انتشار تدریجی؛ و در پایان مقدار پیشفرض (value).
ایجاد و تغییر#
cloudhost flags add new-checkout --type boolean --description "New checkout flow"
cloudhost flags set new-checkout true --environment preview
cloudhost flags rule add new-checkout email ends_with @acme.ir --value true --environment production
cloudhost flags rollout new-checkout 10 --value true --environment production
cloudhost flags listفلگها در داشبورد زیر «فلگها» در نوار کناری پروژه هستند: فهرست، و برای هر فلگ صفحهای با محیطها، قانونها، لغزندهٔ انتشار، آزمایش و تاریخچه. ابزار MCP هم همین کارها را دارد (list_flags، set_flag، kill_flag و بقیه).
خواندن در کد#
در هر فانکشن و برنامه، env.FLAGS هست. بدون توکن و بدون تماس شبکه از خود سرور میخواند. context ویژگیهایی است که قانونها میسنجند؛ مقدار key (یا ویژگیای که برای انتشار تعیین کردهاید) تعیین میکند هر کاربر در کدام گروه باشد.
export default {
async fetch(request, env) {
const user = { key: "user-42", email: "a@acme.ir", plan: "pro" };
// the value; false if there is no such flag
if (await env.FLAGS.get("new-checkout", false, user)) return newCheckout(request);
// every flag at once, or why a flag gave its answer
const all = await env.FLAGS.getAll(user);
const why = await env.FLAGS.evaluate("new-checkout", user); // { value, reason, rule? }
return oldCheckout(request);
},
};| روش | کار |
|---|---|
await env.FLAGS.get(key, default?, context?) | مقدار؛ اگر فلگ نباشد default شما |
await env.FLAGS.getAll(context?) | همهٔ فلگها: {کلید: مقدار} |
await env.FLAGS.evaluate(key, context?, default?) | {value, reason, rule?, variant?, bucket?}؛ reason یکی از off، rule، rollout، default، missing |
محیطها و شاخهها#
پروداکشن از تنظیمات production پیروی میکند و پیشنمایشها از preview. یک شاخه میتواند تنظیمات جدا بگیرد: پیشنمایش آن شاخه بهجای تنظیمات preview از آنها پیروی میکند (شاخه با همان نامی که در git push میدهید).
cloudhost flags set new-checkout true --environment preview --branch feature/checkout
cloudhost flags eval new-checkout --environment preview --branch feature/checkoutقانونها و انتشار تدریجی#
یک قانون شرطهایی دارد که همه باید برقرار باشند و مقداری که در آن صورت داده میشود. شرطها:
| شرط | معنی |
|---|---|
equals, not_equals | ویژگی با یک مقدار برابر باشد یا نباشد |
in, not_in | ویژگی در فهرست باشد یا نباشد |
contains, not_contains | متن شامل زیرمتن باشد؛ اگر ویژگی فهرست باشد، شامل آن عضو |
starts_with, ends_with | متن با زیرمتن شروع یا تمام شود |
- قانونها به ترتیب بررسی میشوند و اولین قانونِ مطابق برنده است؛ با
percentageفقط همان درصد از مطابقها میگیرند. - انتشار تدریجی: هر کلید با یک هش ثابت به عددی از ۰ تا ۹۹٫۹۹ درصد میرسد، پس هر کاربر همیشه در همان گروه میماند، در همهٔ سرورها و در REST. با بالا بردن درصد فقط کاربران اضافه میشوند و کسی که فلگ را داشت از دست نمیدهد.
- ویژگی گروهبندی پیشفرض
keyاست؛ با--byیاbucket_byویژگی دیگری (مثلاًorgId) را بگذارید تا همهٔ اعضای یک سازمان با هم باشند. اگر زمینه آن ویژگی را نداشته باشد، انتشار نادیده گرفته میشود. - با
saltگروهبندی را عوض کنید تا کاربران دیگری انتخاب شوند.
بخشها#
بخش فهرستی از کاربران است که در چند فلگ به کار میرود: کاربرانی که داخلاند، کاربرانی که بیروناند (بیرون بر داخل مقدم است)، و قانونهایی برای بقیه. در قانون فلگ، شرط in_segment یا not_in_segment را با کلید بخش بنویسید.
cloudhost flags segments add staff --include user-1,user-2
cloudhost flags rule add new-checkout key in_segment staff --environment production --value trueکلید قطع اضطراری#
اگر ویژگیای خراب شد، فلگش را قطع کنید: همان لحظه مقدار «حالت قطع» داده میشود و قانونها و انتشار نادیده گرفته میشوند. بدون استقرار مجدد و بدون دستزدن به قانونها؛ بعد از رفع مشکل روشنش میکنید.
cloudhost flags kill new-checkout # every environment
cloudhost flags kill new-checkout --environment production
cloudhost flags revive new-checkoutتاریخچه و بازگشت#
هر تغییر یک نسخه در تاریخچهٔ فلگ ایجاد میکند: چه کسی، چه وقت، چه کاری و کدام محیطها را تغییر داد. بازگشت به یک نسخه کل فلگ را (همهٔ محیطها و شاخهها) به همان حالت برمیگرداند و خودش هم ثبت میشود. تاریخچه بعد از حذف فلگ هم میماند، پس فلگ حذفشده را هم میشود برگرداند.
cloudhost flags history new-checkout
cloudhost flags rollback new-checkout 3خواندن از بیرون پلتفرم#
برای سرور یا ابزار خودتان یک توکن فقطخواندنی ایجاد کنید؛ هر توکن یک محیط را میبیند و نمیتواند چیزی را عوض کند. قانونها نزد ما میمانند و فقط پاسخ برمیگردد.
// flags.js: a small client, no dependencies
const API = "https://api.ibel.ai";
const cache = new Map();
export function flags(token, { ttl = 5000 } = {}) {
const ask = async (key, context) => {
const id = key + JSON.stringify(context ?? {});
const hit = cache.get(id);
if (hit && Date.now() - hit.at < ttl) return hit.value;
const r = await fetch(`${API}/v1/flags/read`, {
method: "POST",
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
body: JSON.stringify({ keys: [key], context }),
});
if (!r.ok) throw new Error(`flags: HTTP ${r.status}`);
const value = (await r.json()).flags[key];
cache.set(id, { at: Date.now(), value });
return value;
};
return {
get: async (key, fallback, context) => (await ask(key, context))?.value ?? fallback,
evaluate: (key, context) => ask(key, context), // { value, reason, rule? }
};
}
// const f = flags(process.env.FLAGS_TOKEN);
// if (await f.get("new-checkout", false, { key: user.id })) { … }OpenFeature#
برنامههایی که بیرون از پلتفرم اجرا میشوند با SDKهای استاندارد OpenFeature و ارائهدهندهٔ OFREP فلگها را میخوانند. یک کلید فلگ ایجاد کنید: کلید سرور همهٔ فلگهای یک محیط را میبیند؛ کلید مرورگر فقط فلگهایی را که برای مرورگر نمایان کردهاید، پس گذاشتنش در صفحهٔ وب امن است.
import { OpenFeature } from "@openfeature/server-sdk";
import { OFREPProvider } from "@openfeature/ofrep-provider";
await OpenFeature.setProviderAndWait(new OFREPProvider({
baseUrl: "https://api.ibel.ai",
headers: [["Authorization", `Bearer ${process.env.FLAG_KEY}`]],
}));
const client = OpenFeature.getClient();
const on = await client.getBooleanValue("new-checkout", false, { targetingKey: user.id, plan: user.plan });targetingKey همان کلید درصد انتشار است، پس هر کاربر همیشه همان پاسخ را میگیرد. هر درخواست یک «درخواست فلگ» شمرده میشود، هر چند فلگ که بخواند؛ مصرف و قیمت در صفحهٔ قیمتها است.سقفها#
| سقف (برای هر برنامه یا فانکشن) | رایگان | حرفهای |
|---|---|---|
| فلگ | 20 | 1,000 |
| قانون در هر فلگ و محیط | 5 | 50 |
| بخش | 2 | 100 |
| تغییر در ماه | 500 | نامحدود |
| نگهداری تاریخچه | ۳۰ روز | یک سال |
| تنظیمات جدای شاخه در هر فلگ | 5 | 100 |
| توکن فقطخواندنی | 2 | 20 |
| خواندن در ثانیه با هر توکن | 10 | 100 |
| درخواست فلگ در ماه (OpenFeature) | 100,000 | 1,000,000 |
هر مقدار تا ۲ کیلوبایت و هر تنظیمات تا ۱۶ کیلوبایت است. GET /v1/pricing همین سقفها را میدهد.