Skip to content

Background adapter contract

The package includes the worker entry point and persistent operation state, but does not bundle a queue, broker, scheduler or Celery dependency. With no adapter, operations over bulk_background_threshold fail before building a plan.

from django.test import RequestFactory
from modelsheet.adapters import BackgroundAdapter

class ProjectBackgroundAdapter(BackgroundAdapter):
    def capture_context(self, request):
        # Include and validate your tenant/project context here.
        return {"tenant_id": request.tenant.pk}

    def restore_context(self, user, context):
        request = RequestFactory().post("/modelsheet-worker/")
        request.user = user
        request.tenant = load_authorized_tenant(user, context["tenant_id"])
        return request

    def enqueue(self, operation_id, phase):
        # Your durable task calls modelsheet.background.process(operation_id, phase).
        project_queue.enqueue("modelsheet.background.process", operation_id, phase)

Set MODELSHEET['background_adapter'] to the adapter's dotted path. Never serialize a Django request, queryset or pickled user into the queue. The entry point reloads an active staff user and registered Admin, then reevaluates permissions and scope.

Phases are preview and run. A preview materializes its full candidate set and values on the worker, then becomes ready for explicit confirmation. Confirmation queues execution of those exact items. Delivery must be at least once; retries and expired-worker recovery belong to the queue integration. Enqueue failure produces dispatch_failed with an operation ID; resume retries dispatch.

Each worker claims a five-minute lease and a fencing token. Execution uses chunks; each row's model write, audit item and completion marker share a transaction. Completed items are never reapplied. After a crash, wait until the lease expires before resuming; a monitor should requeue abandoned operations. A duplicate message cannot execute an unconfirmed plan or rebuild an already completed preview.

Cancellation takes effect between chunks and preserves completed work. Resume continues pending entries, not failed or completed entries. Conflict rows are reported; corrections require a new preview. Synchronous execution is all-or-nothing; background execution can finish partially. There is no automatic rollback of completed chunks. External signals/actions need their own idempotency protocol.

Preview building currently materializes up to max_bulk_rows in worker memory and uses one transaction to publish the plan. Size the limit for your model and database; it is not a streaming engine for unlimited datasets. Aggregates use captured preview inputs. A chunk checks current versions of its own target rows before writing.