کارکردگی کی ٹیلی میٹری جو کسی کی شناخت نہیں کر سکتی
ستمبر میں میں نے CandyRush کے عوامی پرفارمنس صفحے کے پیچھے کی پوری پائپ لائن بنائی: گیم یہ ناپتی ہے کہ وہ کیسے چل رہی ہے، ہر رن کے آخر میں ایک چھوٹا خلاصہ بھیجتی ہے، ایک سرور سیشنز کو یکجا کرتا ہے، اور سائٹ نتیجہ شائع کرتی ہے۔ ڈیزائن کا مقصد یہ تھا کہ پرائیویسی صفحہ ایک مضبوط اور سچی بات کہہ سکے:
ایک ہی کھلاڑی کے دو سیشنز کو آپس میں جوڑا نہیں جا سکتا، نہ ہم جوڑ سکتے ہیں، نہ کوئی اور جو محفوظ شدہ ڈیٹا پڑھے۔
یہ جملہ اس لیے درست ہے کہ نظام اسی طرح بنایا گیا ہے، کسی پالیسی کی وجہ سے نہیں۔ اسے چار خصوصیات سہارا دیتی ہیں:
| خصوصیت | کہاں نافذ ہوتی ہے |
|---|---|
| کوئی مستقل شناخت کنندہ نہیں: لانچ پر بنی ایک بے ترتیب سیشن id، جو بند ہونے پر پھینک دی جاتی ہے | گیم میں payload کی type |
| شناخت کنندے رد کیے جاتے ہیں، ہٹائے نہیں جاتے | ingest endpoint، جو 400 لوٹاتا ہے |
| پہلے ہر سیشن کے percentiles، پھر سیشنز کا میڈین | roll-up سروس |
| 5 سے کم سیشنز سے کچھ شائع نہیں؛ خام سیشنز 5 دن بعد حذف | roll-up سروس اور اسٹوریج lifecycle اصول |
یہ کام سات دنوں میں، 7 سے 13 ستمبر تک، چار ریپوزٹریز میں پانچ مرج شدہ پل ریکوئسٹس کی صورت میں آیا:
| پل ریکوئسٹ | کیا کیا | مرج |
|---|---|---|
| CandyRush #873 | فریم ٹائم histograms، میموری اور لوڈ ٹائمز، prod کے لیے محفوظ uploader | 7 ستمبر |
| PublicWebsite #777 | صرف لکھنے والا ingest endpoint | 7 ستمبر |
| SimVerify #44 | roll-up سروس جو خام سیشنز کو شائع شدہ windows میں بدلتی ہے | 11 ستمبر |
| PublicWebsite #781 | پرفارمنس صفحے پر window picker اور schema 2 | 11 ستمبر |
| UTools #90 | تشخیصی اپ لوڈز اور کریش رپورٹس کے لیے مشترکہ لاگ scrubber | 13 ستمبر |
میں نے وہ issue لکھا جس نے کام کو دو ریپوز میں نو مراحل میں بانٹا، AI کوڈنگ ایجنٹس نے ہر مرحلہ نافذ کیا، اور ہر پل ریکوئسٹ میرے مرج کرنے سے پہلے ایک adversarial جائزے سے گزری۔ دلچسپ حصہ فیصلے ہیں، اس لیے آگے انہی کا ذکر ہے۔
پیمائش کا ریلیز بلڈ میں موجود ہونا ضروری تھا
issue میں کہا گیا تھا کہ موجودہ تشخیصی رپورٹر میں ایک فریم ٹائم histogram شامل کیا جائے۔ لفظ بہ لفظ ایسا کرنے سے وہ اعداد بنتے جو کوئی کھلاڑی کبھی پیدا ہی نہیں کر سکتا: تشخیصی رپورٹرز حقیقی production بلڈ سے compile ہو کر نکال دیے جاتے ہیں، اور production ہی وہ درجہ ہے جسے عوامی ڈیش بورڈ کو بیان کرنا ہے۔
اس لیے histograms نئی، ہمیشہ compile ہونے والی ٹیلی میٹری types میں منتقل ہوئے، اور تشخیصی رپورٹرز اب ان کے مالک ہونے کے بجائے ان سے پڑھتے ہیں۔ پل ریکوئسٹ اسے issue سے ایک دانستہ انحراف کے طور پر، وجہ سمیت، درج کرتی ہے۔
histogram خود مقررہ سائز کا ہے: 0.5 ms کے buckets، 100 ms تک، کل 201 integers، چاہے سیشن پانچ منٹ کا ہو یا پانچ گھنٹے کا، میموری ایک جتنی۔ اسی سے گیم p50، p95، p99 اور p99.9، نیز 1% اور 0.1% lows رپورٹ کرتی ہے۔
دو چھوٹے فیصلے جتنے نظر آتے ہیں اس سے زیادہ اہم تھے:
- ایک نہیں، دو histograms۔ ایک ہر فریم رکھتا ہے؛ دوسرا صرف وہ فریم جو سمولیشن کے چلتے وقت render ہوئے۔ ایک ہی ملا جلا histogram منظر لوڈ ہونے کی رکاوٹ کو tail پر حاوی ہونے دیتا ہے، جو لڑائی کی کارکردگی کے بارے میں کچھ نہیں بتاتی۔ تمام فریمز والا histogram رکھا گیا تاکہ لوڈ کی رکاوٹیں نظر آتی رہیں۔
- "گیم پلے فریم" کا مطلب "اس فریم میں ایک tick چلا" نہیں۔ 144 Hz پر زیادہ تر فریمز میں جائز طور پر کوئی سمولیشن tick نہیں چلتا۔ سادہ اصول تیز فریمز پھینک دیتا ہے اور گیم پلے کی تقسیم تقریباً مکمل طور پر سست فریمز سے بناتا ہے، جو ایک مایوس کن جھکاؤ ہے۔ اس کے بجائے درجہ بندی 12 فریم کی رعایتی مدت اور loop کے اپنے pause فلیگ پر چلتی ہے۔
payload یہ بھی ساتھ لے جاتا ہے کہ ڈسپلے کیا دکھا سکتا ہے: refresh rate، vsync، اور present cap۔ اس کے بغیر 40 Hz کا لیپ ٹاپ ایک بغیر حد والے ڈیسک ٹاپ کے ساتھ مل جاتا ہے اور شائع شدہ میڈین گیم کے بجائے مانیٹرز کو ناپتا ہے۔
گمنامی payload کی type کی خصوصیت کے طور پر
سیشن کی شناخت ایک بے ترتیب عدد سے ہوتی ہے جو گیم شروع ہونے پر بنتا ہے اور بند ہونے پر پھینک دیا جاتا ہے۔ نہ Steam id ہے، نہ hash کی ہوئی Steam id، نہ ڈسک پر محفوظ GUID، اور نہ کوئی ہارڈویئر fingerprint۔ رن کا seed اپنی ساخت کے لحاظ سے ہی غیر موجود ہے۔
hashing پر غور کیا گیا اور جان بوجھ کر رد کیا گیا۔ hash کی ہوئی Steam id پھر بھی ایک مستحکم key ہے: یہ ایک ہی کھلاڑی کے ہر سیشن کو جوڑ دیتی ہے، اور جس کے پاس Steam id ہو وہ اسے دوبارہ نکال سکتا ہے۔ یہ فرضی نام ہے، گمنامی نہیں۔ سیشنز کو ناقابلِ ربط بنانے کا واحد طریقہ یہ ہے کہ کبھی کوئی مستحکم چیز بھیجی ہی نہ جائے۔
اس سے ایک عملی تفصیل نکلتی ہے۔ payload کا JSON، Unity کے JsonUtility کے بجائے ہاتھ
سے لکھا جاتا ہے، کیونکہ JsonUtility کسی field کو چھوڑ نہیں سکتا۔ production payload
میں "deviceModel":"" آ جاتا، اور گمنامی کا دعویٰ wire format میں نظر آنے والی چیز کے
بجائے محض ایک رواج رہ جاتا۔
سرور ہٹاتا نہیں، رد کرتا ہے
ingest endpoint ایک Cloudflare Pages Function ہے جو صرف لکھتا ہے۔ اس کی جانچیں ایک مقررہ ترتیب میں چلتی ہیں: auth، سائز کی حد، content type، headers، rate limit، parse، ہر field کی سخت validation، اور پھر object storage میں لکھنا۔
تین اصول گمنامی کو edge پر قابلِ جانچ بناتے ہیں:
X-Steam-Idheader والی درخواست رد کی جاتی ہے، نظر انداز نہیں کی جاتی۔- جو payload خود کو
prodچینل کا بتائے مگر device model، GPU، OS یا build id ساتھ لائے، اسے خاموشی سے صاف کرنے کے بجائے 400 ملتا ہے۔ - رن کا seed ہر چینل پر رد کیا جاتا ہے۔
رد کیوں؟ جو سرور شناخت کنندے ہٹاتا ہے، وہ سرور انہیں وصول بھی کرتا ہے۔ ہٹانا خاموشی سے ناکام ہوتا ہے: ایک نیا field، ایک بدلا ہوا نام، یا ہٹانے کی فہرست میں ایک ٹائپو، اور قابلِ شناخت ڈیٹا بغیر کسی نشانی کے اسٹوریج میں پہنچنے لگتا ہے۔ جو سرور رد کرتا ہے وہ واضح طور پر ناکام ہوتا ہے: کلائنٹ کا اپ لوڈ ٹوٹ جاتا ہے، اور غلطی ٹیسٹنگ میں سامنے آ جاتی ہے۔
rate limiter بھی ناکامی پر بند ہو جاتا ہے۔ یہ ایک KV binding میں رہتا ہے، اور binding موجود نہ ہو تو جواب 503 ہوتا ہے، "لامحدود" نہیں۔ جو endpoint کسی بھولی ہوئی binding کی وجہ سے خاموشی سے اپنا limiter کھو دے، وہ اس واحد طریقے سے ناکام ہوتا ہے جس کا پتا بل دیکھ کر چلتا ہے۔
خام سیشنز ایک نجی bucket میں پانچ دن کے lifecycle اصول کے ساتھ رہتے ہیں۔ ایک کے بجائے پانچ دن جان بوجھ کر رکھے گئے: خام سیشن اس نظام کی واحد چیز ہے جسے دوبارہ اخذ نہیں کیا جا سکتا، اس لیے roll-up کو ایک ناکام رن کی گنجائش چاہیے۔
فریمز کو ملائے بغیر roll-up
roll-up، SimVerify میں چلتا ہے، جو .NET سروس ہے اور لیڈر بورڈ replays کی تصدیق بھی کرتی
ہے۔ ایک TelemetryRollupService ہر درجے کے لیے ہر پانچ منٹ میں چلتی ہے: یہ اسٹوریج سے نئے
سیشنز لاتی ہے، ہر ایک کو endpoint جیسے ہی اصولوں پر جانچتی ہے، اسے SQLite میں رکھتی ہے،
آج، کل، اس ہفتے اور تمام ہفتوں کے views دوبارہ نکالتی ہے، اور انہیں ایک lastTickUtc
heartbeat کے ساتھ شائع کرتی ہے تاکہ صفحہ رکے ہوئے roll-up اور ایک خاموش دن میں فرق کر سکے۔
اعداد و شمار کا طریقہ اس بات سے طے ہوتا ہے کہ ڈیٹا کیا ہے۔ ہر سیشن پہلے ہی اپنے فریمز کا خلاصہ کر چکا ہوتا ہے، اس لیے roll-up تمام فریمز کو ملا کر حقیقی percentile نہیں نکال سکتا۔ صفحہ ہر سیشن کے اپنے percentile کا سیشنز کے درمیان میڈین شائع کرتا ہے، اور اوسط کو فریمز کی تعداد سے وزن دیتا ہے۔ پانچ سے کم سیشنز سے نکلا کوئی بھی عدد شور کے طور پر شائع کرنے کے بجائے روک لیا جاتا ہے، اور اس حد سے نیچے کے پلیٹ فارمز مکمل طور پر چھوڑ دیے جاتے ہیں۔
پل ریکوئسٹ کی چند تفصیلات:
- ڈیوائس کے fields محفوظ شدہ payload JSON کے اندر رہتے ہیں اور کبھی ڈیٹا بیس کے columns نہیں بنتے، اس لیے کوئی چیز ان کے حساب سے گروپ کرنے والی query کی دعوت نہیں دیتی۔
- aggregator ایک پرانے Python roll-up اسکرپٹ کا port ہے۔ ہر Python ٹیسٹ کا xUnit میں ترجمہ کیا گیا، اور ایک golden ٹیسٹ آٹھ محفوظ شدہ dev سیشنز کے لیے نئی سروس کے نتیجے کو Python اسکرپٹ کے نتیجے سے ملاتا ہے۔
- اشاعت
rclone copyسے ہوتی ہے، کبھیsyncسے نہیں، تاکہ کوئی خراب مقامی حالت پہلے سے شائع شدہ چیز کو حذف نہ کر سکے۔
صفحہ، اور ایک 404 جو exception پھینکتا ہے
ویب سائٹ کی تبدیلی نے ایک window picker شامل کیا (آج، کل، یہ ہفتہ، تمام ہفتے، یا کوئی بھی گزرا ہوا ISO ہفتہ) اور صفحے کو نئے schema پر منتقل کیا۔
adversarial جائزے کی واحد رکاوٹ آسانی سے نظر سے چھوٹ سکتی تھی: اسٹوریج ہوسٹ اپنے عوامی 404
جوابات CORS headers کے بغیر لوٹاتا ہے، اس لیے براؤزر میں fetch قابلِ مطالعہ 404 لوٹانے
کے بجائے exception پھینکتا ہے، اور "ابھی شائع نہیں ہوا" ایک خرابی کے طور پر دکھائی دیتا۔
ٹیسٹس میں اب ایسے fetch stubs شامل ہیں جو exception پھینکتے ہیں، بالکل حقیقی براؤزر کی
طرح۔
تشخیصی لاگز کے لیے وہی اصول
تشخیصی لاگ اپ لوڈز ایک الگ راستہ ہیں، اور پرائیویسی صفحہ انہیں الگ سے بیان کرتا ہے۔ انہیں بھی یہی سلوک درکار تھا۔ CandyRush #905 نے اپ لوڈ کی key اور metadata سے Steam id ہٹا دی تھی، مگر لاگ کا متن بھی اسے چھاپتا ہے، اور save کے ہر path میں OS اکاؤنٹ کا نام ہوتا ہے۔
UTools #90 نے ایک scrubber شامل کیا جسے دو راستے استعمال کرتے ہیں: لاگ uploader اور کریش رپورٹر۔ یہ user paths، Steam ids اور کمپیوٹر کا نام بدل دیتا ہے:
C:\Users\name\... -> C:\Users\<user>\...
7656119XXXXXXXXXX -> <steamid>
the machine's hostname -> <device>
ہر اصول جان بوجھ کر محدود رکھا گیا ہے:
- Steam ids کو ان کے مقررہ
7656119prefix سے پہچانا جاتا ہے، "کوئی بھی 17 ہندسے" سے نہیں، تاکہ ملی سیکنڈ timestamps، build ids اور فریم counters محفوظ رہیں۔ - hostname صرف تب بدلا جاتا ہے جب وہ کم از کم چار حروف کا ہو، تاکہ
PCنام کی مشین غیر متعلقہ متن کو ٹکڑے ٹکڑے نہ کر دے۔ - stack trace کے file paths کو نہیں چھیڑا جاتا۔ یہ compile کے وقت شامل ہونے والے بلڈ مشین کے paths ہیں، کھلاڑی کے نہیں، اور انہیں بدلنے سے symbolication ٹوٹ جاتی۔
ایک دریافت فرض کرنے کے بجائے تصدیق کی گئی: کریش رپورٹر کی event type اپنی breadcrumbs کو
صرف پڑھنے والی collection کے طور پر پیش کرتی ہے، اس لیے BeforeSend hook میں انہیں صاف
کرنا خاموشی سے بے اثر رہتا۔ اس کے بجائے ہر breadcrumb اندر آتے وقت صاف کی جاتی ہے، جس کی
تصدیق گیم کے ساتھ شپ ہونے والی Sentry.dll (4.9.0) پر reflection سے کی گئی۔
ٹیسٹس کی تعداد
ہر پل ریکوئسٹ نے اپنے گیٹ رن کا نتیجہ درج کیا:
| ریپوزٹری | ٹیسٹس |
|---|---|
| CandyRush #873 | 21 میں سے 21 ٹیلی میٹری EditMode ٹیسٹس |
| PublicWebsite #777 | 168 میں سے 168 کامیاب، جن میں 25 نئے endpoint contract ٹیسٹس |
| PublicWebsite #781 | 323 میں سے 323، پہلے 276 |
| SimVerify #44 | 1,015 کامیاب، 0 ناکام، پہلے 926 |
| UTools #90 | 16 نئے scrubber ٹیسٹس |
یہ کیا دکھاتا ہے اور کیا نہیں
گیم کی ریلیز کنفیگریشن میں uploader 7 ستمبر سے آن ہے۔ SimVerify کے qa اور production roll-up درجے 11 ستمبر کو فعال کیے گئے، جب dev درجہ آٹھ حقیقی سیشنز کے ساتھ شروع سے آخر تک ثابت ہو چکا تھا۔ یہ تحریر production ٹریفک کی مقدار کے بارے میں کوئی دعویٰ نہیں کرتی؛ جب تک کسی window میں پانچ سیشنز نہ ہوں، صفحہ اس کے لیے کچھ نہیں دکھاتا، اور مقصد یہی ہے۔
جو بات عمومی طور پر کام آتی ہے
پرائیویسی کو پروسیسنگ کی نہیں، type کی خصوصیت بنائیں۔ اگر payload کی type میں شناخت کنندہ سما ہی نہیں سکتا، تو بعد کا کوئی code path اسے لیک نہیں کر سکتا۔
ہٹائیں نہیں، رد کریں۔ فلٹر خاموشی سے ناکام ہوتا ہے۔ رد کرنا ٹیسٹنگ میں ناکام ہوتا ہے۔
جانیں کہ آپ کا ڈیٹا کس چیز کو سہارا دے سکتا ہے۔ فی سیشن خلاصوں سے ملا ہوا percentile نہیں نکل سکتا، اس لیے صفحہ ایسا ظاہر نہیں کرتا؛ یہ سیشنز کے درمیان میڈین شائع کرتا ہے اور چھوٹے نمونے روک لیتا ہے۔