ویژگی وظیفه یکتا در Asynq مطمئن می‌شود که تنها یک کار در صف Redis وجود دارد.

هنگامی که میخواهید وظایف را تکرار نکنید و از تکرار وظایف جلوگیری کنید، این ویژگی بسیار مفید است.

مرور

برای اطمینان از یکتایی وظایف در Asynq دو روش وجود دارد.

  1. استفاده از گزینه TaskID: تولید شناسه یکتا وظیفه توسط خودتان.
  2. استفاده از گزینه Unique: اجازه دادن به Asynq برای ایجاد قفل یکتایی برای وظیفه.

استفاده از گزینه TaskID

اگر شما از روش اول استفاده کنید، می‌توانید اطمینان حاصل کنید که در هر زمان تنها یک وظیفه با یک شناسه وظیفه داده شده وجود دارد. اگر تلاش کنید که وظیفه دیگری با همان شناسه وظیفه قرار دهید، خطای ErrTaskIDConflict بازگردانده می‌شود.

// وظیفه اول باید بدون مشکل باشد
_, err := client.Enqueue(task, asynq.TaskID("mytaskid"))

// وظیفه دوم شکست می‌خورد، err خطای ErrTaskIDConflict خواهد بود (اگر تنها وظیفه اول هنوز پردازش نشده باشد)
_, err = client.Enqueue(task, asynq.TaskID("mytaskid"))

استفاده از گزینه Unique

روش دوم بر اساس قفل یکتایی است. هنگام قرار دادن یک وظیفه با استفاده از گزینه Unique، Client بررسی می‌کند که آیا می‌تواند قفل برای وظیفه داده شده را بگیرد یا خیر. وظیفه تنها در صورتی قرار داده می‌شود که بتوان قفل را گرفت. اگر وظیفه دیگری قفل را دارد، Client خطا را باز می‌گرداند (در کد نمونه زیر نحوه بررسی خطاها را ببینید).

قفل یکتایی با یک مهلت زمانی (TTL) مرتبط است تا از نگهداشتن قفل برای همیشه جلوگیری شود. قفل پس از گذشت مهلت زمانی یا پس از اینکه وظیفه با موفقیت پردازش شده است، آزاد می‌شود.

یک نکته مهم برای توجه است این است که ویژگی وظیفه یکتا در Asynq از تلاش بهتر برای یکتایی استفاده می‌کند. به عبارت دیگر، اگر قفل قبل از پردازش وظیفه منقضی شود، ممکن است وظیفه تکراری قرار داده شود.

یکتایی وظیفه بر اساس ویژگی‌های زیر است:

  • نوع
  • بار
  • صف

بنابراین، اگر وظایف با همان نوع و بار در همان صف قرار داده شوند، وظیفه دیگری با همین ویژگی‌ها قرار داده نخواهد شد تا قفل آزاد شود.

c := asynq.NewClient(redis)

t1 := asynq.NewTask("example", []byte("hello"))

// t1 قفل یکتایی را برای یک ساعت آینده نگه خواهد داشت.
err := c.Enqueue(t1, asynq.Unique(time.Hour))
switch {
case errors.Is(err, asynq.ErrDuplicateTask):
    // رسیدگی کار تکراری
case err != nil:
    // رسیدگی خطاهای دیگر
}

t2 := asynq.NewTask("example", []byte("hello"))

// t2 نمی‌تواند قرار داده شود زیرا تکراری از t1 است.
err = c.Enqueue(t2, asynq.Unique(time.Hour))
switch {
case errors.Is(err, asynq.ErrDuplicateTask):
    // رسیدگی کار تکراری
case err != nil:
    // رسیدگی خطاهای دیگر
}

در مثال بالا، t2 بعنوان تکراری از t1 قرار داده نخواهد شد. شما می‌توانید از errors.Is برای بررسی مقدار error بازگردانده شده استفاده کنید تا تصمیم بگیرید که آیا آن خطا asynq.ErrDuplicateTask را در بر دارد یا خیر.