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:
Allocation — Generate prefix/suffix, call external service to allocate numeric ID, update register
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_requiredmust betrueTrigger 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_idhas a database unique constraint — no duplicatesFallback: If ID generation fails after max retries, record remains with
functional_record_id = null
4. Data Model
4.1 G2PFunctionalIdGenerationQueue
G2PFunctionalIdGenerationQueueTwo-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
G2PRegister.functional_record_idField 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
G2PRegisterDefinition.functional_id_generation_requiredSetting 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
_enqueue_functional_id_generation logicGuard: 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
IdAffix Data ClassReturned by the domain-specific ID generator. Contains the prefix and suffix to be composed with the numeric ID.
6.2 G2PIdGeneratorInterface
G2PIdGeneratorInterfaceLocation: openg2p-registry-gen2-core/.../interfaces/g2p_id_generator_interface.py
6.3 G2PIdGeneratorFactory
G2PIdGeneratorFactoryLocation: 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:
Load the register record and its definition
Get the domain-specific ID generator service from factory
Call generator to get prefix/suffix
Call external Functional ID Generation Service to allocate numeric ID
Compose functional_record_id = prefix + numeric_id + suffix
Update the register record with the new functional_record_id
Mark allocation as COMPLETED and set updation to PENDING
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:
Queues on record creation — zero blocking; API returns immediately
Two-stage processing — allocation (get ID from external service) then updation (notify service it's in use)
Fault-tolerant — retries on transient failures; fails safely on permanent errors
Extensible — domain-specific prefix/suffix generation via factory
Audited — queue stores all attempts, timestamps, errors
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:
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 includefunctional_record_id(previously null)Optional: Queue status monitoring — staff portal could expose
/register-config/get_id_generation_queue_statusto 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?