Functional ID generation

Functional ID Generation — Feature Design Document

Project: OpenG2P Registry Gen2 Feature: Functional ID Generation Status: Implemented / Documentation Date: 2026-04-10


1. Overview

Functional ID Generation is a two-stage asynchronous process that automatically generates human-readable, domain-specific IDs (e.g., FAR-00001, HH-00045) for registry records, replacing the system-generated UUID with user-facing identifiers.

The feature is opt-in per register (controlled by functional_id_generation_required flag on G2PRegisterDefinition). When enabled, it integrates with an external Functional ID Generation Service to allocate unique numeric IDs and compose them with domain-specific prefixes and suffixes.

The design follows a queue + beat producer + worker pattern with two separate pipelines:

  1. Allocation — Generate prefix/suffix, call external service to allocate numeric ID, update register

  2. Updation — Notify external service that the allocated ID has been used


2. Design Principles

  • Asynchronous: All operations queued immediately; record gets its ID in the background within seconds

  • Fault-tolerant: Retry logic with configurable max attempts; failed items remain in queue for manual intervention

  • Extensible: Prefix/suffix generation is domain-specific (extensions provide implementations)

  • Stateless workers: All data needed for processing is stored in the queue item

  • Immutable audit trail: Queue stores all attempts, timestamps, and error codes


3. Scope and Constraints

  • Opt-in per register: Register metadata functional_id_generation_required must be true

  • Trigger point: Queue populated after a new record is created (via change request approval)

  • External dependency: Calls a remote Functional ID Generation Service via HTTP

  • Unique constraint: functional_record_id has a database unique constraint — no duplicates

  • Fallback: If ID generation fails after max retries, record remains with functional_record_id = null


4. Data Model

4.1 G2PFunctionalIdGenerationQueue

Two-stage queue item tracking both allocation and updation phases. The same row transitions through both pipelines sequentially.

Location: openg2p-registry-gen2-core/.../models/g2p_functional_id_generation_queue.py

Unique constraint: (register_id, internal_record_id) — one queue item per record per register.

Status transitions:

Allocation phase:

Updation phase (only if allocation succeeded):


4.2 G2PRegister.functional_record_id

Field already exists on the base G2PRegister model:

Remains nullable=True because:

  • New records start without an ID

  • Failed ID generation leaves it null

  • Records created before the feature was enabled may not have IDs


4.3 G2PRegisterDefinition.functional_id_generation_required

Setting this to true enables ID generation for the register. The queue population logic checks this flag before creating a queue item.


5. Trigger: Record Creation

The queue is populated in the post-approval hook when a new record is created. In the registry, all record mutations go through change requests, so the trigger is in G2PRegisterService.approve_change_request().

Trigger Logic

_enqueue_functional_id_generation logic

Guard: Only queued for new records, not for edits. In the registry, edit CRs do not generate new IDs — the functional_record_id remains stable across the record's lifetime.


6. Interface and Factory

6.1 IdAffix Data Class

Returned by the domain-specific ID generator. Contains the prefix and suffix to be composed with the numeric ID.

6.2 G2PIdGeneratorInterface

Location: openg2p-registry-gen2-core/.../interfaces/g2p_id_generator_interface.py

6.3 G2PIdGeneratorFactory

Location: openg2p-registry-gen2-core/.../interfaces/g2p_id_generator_factory.py or in extensions

Actually implemented in extensions (discovered dynamically). The factory is a BaseService singleton:


7. Allocation Stage: Beat Producer + Worker

7.1 Allocation Beat Producer

Location: openg2p-registry-gen2-celery/.../tasks/functional_id_allocation_beat_producer.py

Polls for PENDING allocation items and queues them to the allocation worker.

7.2 Allocation Worker

Location: openg2p-registry-gen2-celery/.../tasks/functional_id_allocation_worker.py

The critical worker. Does the following:

  1. Load the register record and its definition

  2. Get the domain-specific ID generator service from factory

  3. Call generator to get prefix/suffix

  4. Call external Functional ID Generation Service to allocate numeric ID

  5. Compose functional_record_id = prefix + numeric_id + suffix

  6. Update the register record with the new functional_record_id

  7. Mark allocation as COMPLETED and set updation to PENDING

  8. Retry logic on error

7.3 External ID Generation Service Integration

The worker makes an HTTP POST call to an external service to allocate a numeric ID:

Configuration:

The {id_type} placeholder is replaced with the resolved prefix (e.g., "FAR-").


8. Updation Stage: Beat Producer + Worker

8.1 Updation Beat Producer

Location: openg2p-registry-gen2-celery/.../tasks/functional_id_updation_beat_producer.py

Polls for PENDING updation items and queues them to the updation worker. Triggered only after allocation completes.

8.2 Updation Worker

Location: openg2p-registry-gen2-celery/.../tasks/functional_id_updation_worker.py

Notifies the external service that an ID has been used. Currently a placeholder implementation.


9. Beat Schedule and Configuration

9.1 Beat Schedule

Both beat producers are registered with Celery Beat:

Default frequency is ~20 seconds (configurable).

9.2 Worker Constants

Location: celery/.../utils/workers.py

9.3 Configuration

Location: celery-beat-producers/src/.../config.py and celery-workers/src/.../config.py


10. Extension Implementation

Directory Structure

Example: Farmer Extension

The factory discovers and instantiates this class at runtime via importlib.


11. End-to-End Data Flow


12. Retry Logic and Error Handling

Both allocation and updation workers use the same retry strategy:

Failed items remain in the queue indefinitely. They require manual intervention or operator scripts to reset the status or remove them.


13. Retry Configuration

Location: celery-workers/src/.../config.py

Environment variable: REGISTRY_CELERY_WORKERS_WORKER_MAX_ATTEMPTS

Reasonable value: 5 attempts (covers transient network failures).


14. Unique Constraint Implications

The unique=True constraint on functional_record_id means:

  • Can't have duplicates: If two records somehow get the same functional ID, the second update fails

  • Retry-safe: If allocation succeeds but the update fails, retrying is safe (upsert semantics)

  • Conflicts with external service: If the external service allocates the same ID twice (bug), the second allocation fails

To prevent this, ensure the external ID service is stateless and deterministic (for a given id_type, always return the next sequential ID).


15. Summary

The Functional ID Generation feature implements a robust, async pipeline that:

  1. Queues on record creation — zero blocking; API returns immediately

  2. Two-stage processing — allocation (get ID from external service) then updation (notify service it's in use)

  3. Fault-tolerant — retries on transient failures; fails safely on permanent errors

  4. Extensible — domain-specific prefix/suffix generation via factory

  5. Audited — queue stores all attempts, timestamps, errors

  6. Configurable — frequency, retry limits, external service URL all via env vars

The design enables registries to integrate with enterprise ID management systems (e.g., Aadhaar-based ID services, custom sequential ID generators) without coupling to the registry core.


16. Configuration Checklist

Before deploying, ensure these are configured:

Setting
Location
Example
Notes

functional_id_generation_url

workers config

http://id-service:8080/v1

External service base URL

id_generation_allocation_path

workers config

/idgenerator/{id_type}/id

Path template; {id_type} = prefix

id_generation_updation_path

workers config

/idgenerator/mark-used

Path for updation endpoint (TBD)

worker_max_attempts

workers config

5

Retry limit

functional_id_allocation_beat_producer_frequency

beat config

20

Seconds; None = use default

functional_id_updation_beat_producer_frequency

beat config

20

Seconds; None = use default

functional_id_generation_required

register metadata

true/false

Per register; opt-in


17. API Considerations

The feature is primarily async background processing. The only user-facing API changes:

  • GET /register-data/get_subject_record — will now include functional_record_id (previously null)

  • Optional: Queue status monitoring — staff portal could expose /register-config/get_id_generation_queue_status to show pending allocations

Currently no user-facing API to manually trigger ID generation. Queue population happens automatically on CR approval.

Last updated

Was this helpful?