کنسول SQL

پرسشی که یک اپراتور واقعاً با آن میآید این است: «سفارش ۴۴۷۱ کجا رفت؟». آرتمیس نمیتواند پاسخش را بدهد. تنها فیلتر سمت سرورش JMS selector است که هدرها و ویژگیهای اپلیکیشن را میبیند — نه بدنه را — و هر بار روی یک صف در یک نود اعمال میشود.
کنسول SQL پاسخش را میدهد، روی همهٔ صفهای کلاستر، در یک کوئری.
SELECT * FROM "ORDER.*"
WHERE body->>'orderId' = '4471'
LIMIT 50یک گویش واقعی، زیرمجموعهای کوچک
فقط SELECT. یک FROM. WHERE، ORDER BY، LIMIT. بدون join، بدون subquery، بدون union، بدون CTE — و هیچ تغییری اصلاً قابل بیان نیست.
نحو، عمداً SQL واقعی است تا گرامر، رنگآمیزی و تکمیل آمادهٔ ادیتور بدون تغییر کار کنند. کوئریها به AST تجزیه و گرهبهگره در برابر یک کاتالوگ ستون ثابت اعتبارسنجی میشوند، با ردِ پیشفرض. هیچچیز با تطبیق الگو روی متن کوئری اعتبارسنجی نمیشود؛ یک عبارت باقاعده که نبودِ DROP را ادعا کند، همان روشی است که این کار معمولاً با آن اشتباه انجام میشود.
نام صفها همیشه داخل گیومهٔ دوتایی میآیند — ORDER.IN هم واژهٔ رزرو است و هم نقطه دارد — و wildcardها از آنِ آرتمیساند نه SQL: * یک سطحِ نقطهجدا و # چند سطح را میگیرد.
کوئری از کجا میخواند
پیشوند schema، backend را در خودِ متن کوئری انتخاب میکند؛ پس کوئریای که در یک تیکت paste میشود همچنان میگوید از کجا خوانده است.
FROM broker."Q" | بروکرهای زنده. حقیقتِ اکنون. |
FROM index."Q" | ایندکس تاریخی — هنوز پیامهایی را دارد که از آن پس مصرف شدهاند، و فقط صفهایی را پوشش میدهد که برایشان اشتراک گرفتهاید. |
FROM "Q" | planner انتخاب میکند و plan میگوید کدام را برداشته است. |
هزینهٔ یک predicate — پیش از اجرا
این بخشی است که ارزش دانستن دارد. هر جزء AND در سطح بالا طبقهبندی میشود:
| رده | معنا |
|---|---|
| Target | انتخاب میکند کدام صفها خوانده شوند. هزینهای ندارد. |
| Pushdown | به یک JMS selector تبدیل میشود، پس بروکر فیلتر میکند و Studio پیامهای نامنطبق را اصلاً نمیبیند. هزینهای ندارد. |
| Scan | Studio باید هر پیامی را که بروکر برگردانده بررسی کند. |
هدرها و ویژگیهای اپلیکیشن pushdown میشوند. بدنه هرگز نمیتواند — جز Studio هیچچیز نمیتواند آن را بخواند. نوار plan بالای ادیتور پیش از اجرا میگوید کوئری شما کدامیک است، و بالاتر از سقف هزینهٔ پیکربندیشده، کوئری همراه با برآورد، سقف، و راهنمایی برای باریکترکردن آن رد میشود. رد میشود و بریده نمیشود: نتیجهٔ بریدهشده در یک نگاه از نتیجهٔ کامل قابل تشخیص نیست، و اپراتوری در میانهٔ یک حادثه آن را «نیست» میخواند.
یک تلهٔ گفتنی
predicate ای که بهتنهایی رایگان است، درون یک OR کنار یک predicate روی بدنه دیگر رایگان نیست. pushdown کردن یک طرف OR مجموعهای را که طرف دیگر میبیند باریک میکند — پس یک عبارت فصلی که شامل یک scan باشد، در کل scan میشود. این تنها اشتباه این حوزه است که بهجای پاسخ کند، پاسخ بیصدا نادرست میدهد.
ستونها
سازگار با selector (رایگان): priority، durable، timestamp، expiration، size، jmsType، correlationId، groupId، userId، و هر ویژگی اپلیکیشن بهصورت props.<name>.
Target (رایگان): queue، address، node.
Scan: body، messageId، messageType، replyTo، و — فقط روی ایندکس — observedAt و lastSeenAt.
یک فیلد JSON درون بدنه بهصورت body->>'orderId' نوشته میشود. تنها توابعی که گویش میپذیرد now()، lower() و upper() هستند، بهعلاوهٔ interval در زمان نسبی. زمان نسبی از طریق اختلاف ساعتِ اندازهگیریشدهٔ هر نود نرمالسازی میشود، پس بروکری با ساعت منحرف هم درست پاسخ میدهد.
نمونهها
-- ارزانترین حالت: بدون هیچ predicate، بروکر صفحههای ابتداییاش را برمیگرداند.
SELECT * FROM "ORDER.IN" ORDER BY timestamp DESC LIMIT 100;
-- هر دو predicate به یک selector روی یک wildcard تبدیل میشوند. هنوز رایگان.
SELECT * FROM "ORDER.*" WHERE priority > 4 AND durable = true LIMIT 200;
-- ویژگیهای اپلیکیشن هم سازگار با selector هستند.
SELECT * FROM "ORDER.IN" WHERE props.tenant = 'acme' LIMIT 200;
-- یک scan. اول با یک predicate روی هدر باریکش کنید.
SELECT * FROM "ORDER.IN" WHERE body LIKE '%4471%' LIMIT 50;
-- یک پنجرهٔ زمانی، کوانتیده و تصحیحشده برای انحراف ساعت.
SELECT * FROM "ORDER.*"
WHERE timestamp > now() - interval '2 hours'
ORDER BY timestamp DESC LIMIT 500;ایندکس اختیاری
ایندکس پیام بهازای هر صف انتخابی، محدود به دورهٔ نگهداری، و دورانداختنی است. وجود دارد تا بتوان دربارهٔ پیامی پرسید که پیشتر مصرف شده — چیزی که بروکرها، بهدرستی، دیگر هیچ نمیدانند. از نظر کنترل دسترسی و حذف، همچون payload نگهداشتهشده با آن رفتار میشود، و انداختنش تاریخچه را میگیرد نه حقیقت را.
tail زنده
tail یک poll است، هرگز یک consume نیست. نمیتواند چیزی را تغییر دهد و با مصرفکنندههای شما رقابت نمیکند. رابط کاربری این را بهطور دائمی اعلام میکند، چون یک tail نمونهای که مثل ضبط کامل خوانده شود، راهی است برای رسیدن به این نتیجهٔ نادرست که پیامی هرگز ارسال نشده است.
اقدام روی یک نتیجه
نمیتوانید. عمداً. اقدام روی یک سطر نتیجه از مسیر عملیات عادی پیام میگذرد که از پیش dry run، سقف انبوه و رکورد ممیزی را دارد — مسیر دومی به سمت یک فعل مخرب، جای دومی برای اشتباهکردن در قرارداد ایمنی است.