دسکتاپ، Worker و محاسبات محلی · فصل ۵ — Reconciliation، Rollout و Watchdog · 189
Desired State و Drift Reconciliation
اپ یا worker محلی را اجرا میکند اما device identity، privilege، offline state و fleet behavior را طراحی نکرده است. موضوع جاری: «Desired State و Drift Reconciliation». میتواند «Desired State و Drift Reconciliation» را برای یک node محلی با identity، state، privilege و recovery boundary مناسب طراحی کند.
خلاصهٔ درس
اپ یا worker محلی را اجرا میکند اما device identity، privilege، offline state و fleet behavior را طراحی نکرده است. موضوع جاری: «Desired State و Drift Reconciliation».
میتواند «Desired State و Drift Reconciliation» را برای یک node محلی با identity، state، privilege و recovery boundary مناسب طراحی کند.
خروجی این درس
میتواند «Desired State و Drift Reconciliation» را برای یک node محلی با identity، state، privilege و recovery boundary مناسب طراحی کند.
پیشنیازها
1. Process alive به معنی Desired State نیست
Node N-24 میتواند heartbeat سالم بدهد اما worker روی build 7.3 باشد، در حالی که desired generation میگوید build 7.4 و config digest برابر D7 لازم است. بنابراین «process alive» فقط یک observation محدود است؛ reconciliation باید exact desired binding، fresh observed snapshot و post-action proof داشته باشد. یک command موفق یا service status سبز، بهتنهایی convergence را ثابت نمیکند.
2. مرز مالکیت LOC-016
این درس فقط Desired-State Drift Reconciliation Ledger v1 را مالک است: desired state → fresh observation → ownership classification → authorized/idempotent action → fresh convergence probe. rollout مرحلهای در LOC-017 است و backup/watchdog در LOC-018. اگر evidence از آن درسها وارد gate شود، فقط بهعنوان ورودی استفاده میشود؛ منطق آنها اینجا بازتعریف نمیشود.
3. Desired State را با generation و digest ببند
desired_generation نشان میدهد با کدام نسخه منطقی از intent کار میکنی و desired_digest محتوای همان intent را bind میکند. اگر فقط نام نسخه یا generation را نگه داری ولی digest عوض شده باشد، repair میتواند بر مبنای intent دیگری اجرا شود. هر تصمیم باید به هر دو مقدار و provenance قابلردیابی متصل باشد.
4. Observation باید Fresh باشد
Heartbeat تازه، freshness کل snapshot را ثابت نمیکند. observed_at و observation_ttl باید مشخص کنند آیا package version، config، service settings و policy inputها هنوز برای تصمیم معتبرند یا نه. اگر snapshot از TTL عبور کرده باشد، drift هرچقدر واضح بهنظر برسد، تصمیم repair باید BLOCKED بماند تا observation تازه تولید شود.
5. Drift را قبل از Action طبقهبندی کن
هر difference یک repair target نیست. برای هر resource_key، مالکیت را جدا کن: MANAGED برای فیلدهایی که MAKNOW مجاز به reconcile است، OPERATOR_OWNED برای overrideهایی که انسان/عملیات مالک آن است، و UNKNOWN وقتی مالکیت اثبات نشده. overwrite فوریِ هر تفاوت، مرز اختیار را نابود میکند.
6. Operator-owned exception را حفظ کن
فرض کن همه فیلدهای مدیریتشده با desired state match هستند اما maintenance mode بهصورت محلی و تأییدشده تغییر کرده است. reconcile loop نباید برای «زیبا کردن diff» آن را برگرداند. Ledger باید exception و provenance آن را ثبت کند و managed convergence را جدا از operator-owned state بسنجد.
7. Validation را از Enforcement جدا نگه دار
Google VM Manager در OS Policy بین Validation و Enforcement تفکیک میکند؛ DSC نیز Get/Test/Set را جدا میکند. اینها reference pattern هستند: اول تشخیص بده، بعد مجوز و precondition را بسنج، سپس فقط اگر boundary اجازه داد action انجام بده. Detection بهتنهایی authorization نیست.
8. Action باید Idempotent و Content-bound باشد
authorized_action باید به resource و desired digest متصل باشد و idempotency_key تکرار امن همان intent را ممکن کند. اگر همان desired generation پس از convergence دوباره replay شد، loop نباید تغییر جدیدی بسازد؛ نتیجه باید نشان دهد state قبلاً converged است و action مجدد لازم نیست.
9. Retry باید Budget و Backoff داشته باشد
Retry بینهایت reconciliation نیست؛ oscillation پنهان است. attempt_count، retry_budget و backoff را ثبت کن. اگر post-action observation هنوز با desired digest فرق دارد اما budget باقی است، state میتواند RETRY باشد. وقتی budget تمام شد یا writer متعارض ادامه داد، loop باید BLOCKED یا CONFLICT شود.
10. Conflicting Writer را با Drift ساده اشتباه نگیر
اگر یک managed field بلافاصله پس از repair دوباره توسط writer دیگری تغییر کند، مشکل صرفاً «drift باقیمانده» نیست. باید evidence writer conflict ثبت شود و repair loop متوقف شود؛ ادامه overwrite میتواند دو controller را وارد oscillation کند. CONFLICT یک state واقعی است، نه خطایی که با retry بیشتر مخفی شود.
11. Command Success با Convergence فرق دارد
ممکن است package installer exit code صفر بدهد اما snapshot بعدی هنوز digest قدیمی را نشان دهد. آن command فقط execution evidence است. post_action_observation باید fresh باشد و convergence_status را از observed state استخراج کند. تا وقتی desired و observed روی owned fields match نشدهاند، CONVERGED مجاز نیست.
12. Ledger باید Reason و Recheck Trigger داشته باشد
برای هر تصمیم، reason و recheck_trigger لازم است. BLOCKED بدون اینکه بگوید چه evidenceی باید تازه شود، عملیاتی نیست. مثال: «snapshot stale؛ بعد از refresh observation دوباره ارزیابی کن» یا «conflicting writer؛ پس از resolve مالکیت controller دوباره بررسی کن».
13. Worked Example: Build 7.3 و Config D7
N-24 باید build 7.4 و digest D7 داشته باشد. snapshot تازه build 7.3 را نشان میدهد و maintenance override نیز وجود دارد. build یک managed drift است؛ maintenance override operator-owned است. repair فقط build را تغییر میدهد، سپس snapshot تازه build 7.4 + D7 را اثبات میکند و override دستنخورده میماند. حالا managed state میتواند CONVERGED شود.
14. Failure Probeهای قطعی
چهار mutation را اجرا کن: snapshot را از TTL قدیمیتر کن ولی heartbeat را سالم نگه دار؛ فقط operator-owned maintenance field را تغییر بده؛ همان desired generation را پس از convergence replay کن؛ و command success بده ولی post-action digest را متفاوت نگه دار. خروجی باید بهترتیب freshness، ownership، idempotency و observed convergence را مستقل بسنجد؛ هیچ shortcut نباید success بسازد.
15. Transfer: بازگشت Node پس از دو روز Offline
Node دو روز آفلاین بوده و با desired generation قدیمی برمیگردد، در حالی که یک maintenance override محلی نیز تأیید شده است. اول desired authority را refresh کن، سپس snapshot تازه بگیر و ownership را دوباره طبقهبندی کن. baseline decision را کپی نکن. اگر desired generation محلی قدیمی است، تا refresh شدن intent تصمیم BLOCKED میماند؛ override معتبر نیز نباید overwrite شود.
ارزیابی
پاسخنامهی این درس در بستهی آموزش وجود ندارد؛ خودت جواب بده و صادقانه علامت بزن. «نیاز به مرور» درس را به صف تکرار میفرستد.
F1 retrieval
F2 diagnosis
S1 application
S2 diagnosis
S3 decision
S4 transfer
ملاکهای ارزیابی (rubric)
| Dimension | Sufficient evidence | Blocking failure |
|---|---|---|
| Desired binding | exact desired generation and digest are traceable | label or generation is used without content binding |
| Fresh observation | observed_at and TTL prove decision-time freshness | heartbeat substitutes for full-state freshness |
| Ownership | managed, operator-owned, and unknown fields remain distinct | every diff is overwritten automatically |
| Idempotent bounded action | action has preconditions, idempotency key, retry budget, and backoff | unbounded retry or duplicate effect is allowed |
| Convergence + transfer | fresh post-action evidence proves owned convergence and transfer is recomputed | command success or copied baseline decision is treated as proof |
منابع و تازگی
مطالعهٔ موارد (case studies)
تصاویر درس
فایلهای آزمایشگاه
Implement evaluate(case) in starter.py using only the visible synthetic evidence.
Return these fields: desired_binding, observation_state, drift_state, action_state, convergence_status, decision, audit_event, and reason.
Do not infer convergence from heartbeat or command success. Preserve operator-owned fields, stale observations, conflicting writers, and retry limits explicitly.