المستوى: متوسط — يفترض إنك بتشتغل Terraform على فريق وعندك backend بعيد (remote state).
قفل حالة Terraform: ليه اتنين apply في نفس الوقت بيخربوا الـ state
قفل الحالة (state locking) بيمنع أسوأ عطل في Terraform: تلف ملف الـ state لما اتنين بيعملوا apply في نفس اللحظة. سطر إعداد واحد بيحوّل الفوضى دي لطابور منظّم، وبيوفّر عليك ساعات استرجاع.
المشكلة باختصار
فريقك بيشغّل terraform apply من أكتر من مكان: مهندس من اللابتوب، وبايبلاين CI في نفس الوقت. الاتنين بيكتبوا على نفس ملف الـ state. النتيجة: ملف متضارب، موارد متسجّلة مرتين، أو موارد اتعملت في السحابة ومش موجودة في الـ state. ساعتها Terraform بيبقى أعمى عن نص بنيتك.
ليه ملف الـ state حسّاس لهذه الدرجة
تخيّل دفتر حسابات واحد على المكتب، واتنين محاسبين بيكتبوا فيه في نفس اللحظة. السطور بتتداخل والأرقام بتبوظ، ومحدش يعرف الرصيد الصح. ملف الـ state زي الدفتر ده بالظبط.
علميًا: ملف الـ state ملف JSON بيمثّل الخريطة بين الموارد في الكود والموارد الفعلية عند المزوّد، يعني aws_instance.web في الكود مربوط بـ i-0abc123 الحقيقي. Terraform بيقرأ الملف قبل أي تغيير عشان يعرف الموجود، وبيكتبه بعد التغيير. لو عمليتين كتبتا عليه بالتوازي، الكتابة الأخيرة بتدهس الأولى، وبتفقد موارد من التتبّع. ده مش bug في Terraform، ده سباق كتابة (write race) على مورد مشترك.
الحل: قفل الحالة (state locking)
الفكرة بسيطة: قبل ما Terraform يلمس الـ state، بياخد قفل. أي عملية تانية تحاول تشتغل في نفس الوقت بتترفض لحد ما القفل يتحرّر. ده mutex موزّع على ملف الـ state.
الطريقة الكلاسيكية على AWS: backend بتاع S3 لتخزين الـ state، وجدول DynamoDB للقفل. أول حاجة، اعمل جدول القفل مرة واحدة:
# جدول القفل لازم يكون مفتاحه الأساسي LockID من نوع String
aws dynamodb create-table \
--table-name terraform-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region eu-west-1
وبعدين اربط الـ backend بالجدول ده:
# backend.tf
terraform {
backend "s3" {
bucket = "myorg-tfstate-prod"
key = "network/terraform.tfstate"
region = "eu-west-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
دلوقتي اللي بيحصل فعلاً عند كل apply: Terraform بيكتب صف في DynamoDB باستخدام conditional write (لو الصف مش موجود). الكتابة دي ذرّية (atomic)، فأول واحد بس بياخد القفل. التاني بيتصدّ.
لو زميلك بدأ apply قبلك بثانية، رسالتك هتبقى كده:
Error: Error acquiring the state lock
Lock Info:
ID: 8f3c1d2a-7e44-4b9c-9f0e-1a2b3c4d5e6f
Operation: OperationTypeApply
Who: sara@laptop
Created: 2026-06-21 09:14:02 UTC
دي مش مشكلة، دي الحماية شغّالة. تستنى ثوانٍ وتعيد المحاولة.
الطريقة الأحدث: قفل S3 من غير DynamoDB
من إصدار Terraform 1.10، الـ backend بتاع S3 بقى بيدعم القفل عبر lock file جوه نفس الـ bucket، فمابقاش لازم جدول DynamoDB منفصل. بتفعّله بسطر واحد:
terraform {
backend "s3" {
bucket = "myorg-tfstate-prod"
key = "network/terraform.tfstate"
region = "eu-west-1"
encrypt = true
use_lockfile = true
}
}
المكسب: مورد أقل تديره، وفاتورة أبسط. التكلفة: عشان S3 صار strongly consistent، القفل ده موثوق، بس لو عندك إعداد قديم متعوّد على DynamoDB، الانتقال محتاج خطوة تنظيف.
أرقام من حالة واقعية
الافتراض إن عندك فريق 8 مهندسين، ملف state بحجم 4MB و1,200 مورد، وبتعملوا حوالي 30 apply في اليوم بين CI ويدوي. قبل تفعيل القفل، السباق على الـ state كان بيحصل تقريبًا مرة كل شهر، وكل حادثة كانت بتاخد 2 إلى 3 ساعات استرجاع يدوي بـ terraform import وتصليح الملف. ده تقدير مبني على فريق متوسط الحجم.
بعد القفل: صفر حالات تلف. تكلفة DynamoDB بنظام الطلب تقريبًا أقل من سنت في الشهر للأحجام دي. الثمن الوحيد: كل apply بياخد زيادة 200 إلى 400 ملّي ثانية في أخذ القفل وتحريره. رقم تافه مقابل ساعة استرجاع.
الـ trade-offs اللي لازم تعرفها
- القفل بيسلسل العمليات. لو عندك apply بياخد 12 دقيقة، الفريق كله بيستنى. المكسب أمان، الخسارة توازي. قسّم الـ state لوحدات أصغر (per-service) عشان تقلّل التزاحم.
- الأقفال العالقة (stale locks). لو العملية ماتت فجأة (انقطاع نت، Ctrl+C)، القفل ممكن يفضل. بتشيله يدويًا بـ
terraform force-unlock 8f3c1d2a-...بعد ما تتأكد إن مفيش حد فعلاً شغّال. - مورد إضافي تديره لو ماشي على DynamoDB. الطريقة الأحدث بـ
use_lockfileبتشيل العبء ده.
متى لا تستخدم هذه الطريقة
لو إنت مطوّر فردي شغّال state محلي على جهازك من غير CI ولا فريق، القفل الموزّع زيادة مش محتاجها. وكمان لو إنت على Terraform Cloud أو Spacelift أو Atlantis، المنصات دي بتدير القفل نيابة عنك تلقائيًا، فمتزوّدش طبقة فوقها. القفل ضروري بس لما يكون فيه أكتر من كاتب محتمل على نفس الـ state.
الخطوة التالية
افتح ملف الـ backend بتاعك دلوقتي. لو على AWS وإصدارك 1.10 أو أحدث، ضيف use_lockfile = true وشغّل terraform init -reconfigure ثم terraform plan. شغّل أمرين plan في نفس الوقت من تيرمنالين، ولازم واحد منهم ياخد رسالة Error acquiring the state lock. لو شفتها، القفل شغّال صح.
المصادر
- HashiCorp — State Locking:
developer.hashicorp.com/terraform/language/state/locking - HashiCorp — S3 Backend (dynamodb_table و use_lockfile):
developer.hashicorp.com/terraform/language/backend/s3 - HashiCorp — أمر force-unlock:
developer.hashicorp.com/terraform/cli/commands/force-unlock - AWS — DynamoDB conditional writes:
docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithItems.html