Skip to content

fix: make process ID configurable to resolve Docker PID issue (#30) - #31

Open
aevitas wants to merge 3 commits into
masterfrom
fix/configurable-process-id
Open

aevitas wants to merge 3 commits into
masterfrom
fix/configurable-process-id

Conversation

@aevitas

@aevitas aevitas commented Jul 19, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #30

In containerized environments such as Docker, the process ID of the entrypoint is almost always 1. Since FlakeId uses the process ID as part of its uniqueness composite (5 bits), every Dockerized instance shares the same process ID component, reducing the uniqueness space and risking collisions when running multiple containers.

This PR introduces two ways to configure a custom process ID:

1. Static Property (Id.ProcessId)

// Set at application startup, before any IDs are generated:
Id.ProcessId = 7; // Must be in the range 0-31 (5 bits); values are automatically masked.

// Revert to automatic detection:
Id.ProcessId = null;

2. Environment Variable (FLAKEID_PROCESS_ID)

docker run -e FLAKEID_PROCESS_ID=7 my-app

Resolution Precedence

When multiple sources are configured, the process ID is resolved in the following order:

  1. The value set through Id.ProcessId
  2. The FLAKEID_PROCESS_ID environment variable
  3. The OS-assigned process ID (existing behavior — unchanged)

The resolved value is masked to 5 bits (range 0–31) as before.

Changes

  • src/FlakeId/Id.cs

    • Added s_configuredProcessId static field.
    • Added ProcessIdEnvironmentVariable internal constant ("FLAKEID_PROCESS_ID").
    • Added public static ProcessId property (get/set) with XML documentation.
    • Extracted process ID resolution into a new ResolveProcessId() private method that checks the property → env var → OS PID.
    • All existing comments and documentation are preserved.
  • README.md

    • Added a new "Process ID Configuration" section between "Timestamps" and "Why create FlakeId?" documenting both configuration methods and the precedence rules.
    • All existing content is preserved.
  • src/FlakeId.Tests/ProcessIdTests.cs (new file)

    • ProcessId_WhenSet_IsUsedInIdGeneration — verifies the configured PID appears in generated IDs.
    • ProcessId_WhenSetToNull_FallsBackToOsProcessId — verifies fallback to OS PID.
    • ProcessId_WhenSetOutOfRange_IsMaskedCorrectly — verifies values exceeding 5 bits are masked (e.g., 100 & 31 = 4).
    • ProcessId_WhenSetViaEnvironmentVariable_IsUsedInIdGeneration — verifies env var is picked up.
    • ProcessId_WhenSetViaProperty_TakesPrecedenceOverEnvironmentVariable — verifies precedence.
    • ProcessId_Get_ReturnsConfiguredValue — verifies the getter returns what was set.
    • Test cleanup restores original ProcessId and env var state, and resets internal s_prevId / s_processId caches.
  • .gitattributes (new file)

    • Added * text=auto eol=lf to normalize line endings across the repository. This prevents future line-ending diff noise.

⚠️ Note on Id.cs Diff

The original Id.cs on master uses CRLF (Windows) line endings, while the content pushed through the GitHub API uses LF (Unix) line endings. This causes the diff to show every line as changed (-/+), even though most lines are identical.

The actual code changes in Id.cs are only:

  1. New fields_configuredProcessId (with 3 comment lines)
  2. New constantProcessIdEnvironmentVariable (with 2 comment lines)
  3. New propertyProcessId get/set (with XML doc comments)
  4. One line changed in CreateInternal()Process.GetCurrentProcess().Id & ProcessIdMaskResolveProcessId()
  5. New methodResolveProcessId() (with XML doc comments)

That's ~40 lines of actual additions and 1 changed line out of the 278-line file. The rest is purely a CRLF → LF line-ending conversion.

The .gitattributes file added in this PR will prevent this issue for all future commits.

Backward Compatibility

This is a non-breaking change:

  • If neither the property nor the environment variable is set, behavior is identical to before (OS process ID is used).
  • The new ProcessId property defaults to null, meaning no configuration is required for existing users.
  • No existing public APIs are changed.

aevitas added 3 commits July 19, 2026 18:26
In containerized environments such as Docker, the process ID is almost
always 1, which reduces the uniqueness guarantee of generated Snowflake
IDs when running multiple containers.

This change introduces two ways to configure a custom process ID:

1. A static `Id.ProcessId` property that can be set programmatically
   at application startup.

2. A `FLAKEID_PROCESS_ID` environment variable, which is especially
   convenient for containerized deployments (e.g., `docker run -e
   FLAKEID_PROCESS_ID=7 my-app`).

The process ID is resolved in the following precedence:
  a. The value set through `Id.ProcessId`
  b. The `FLAKEID_PROCESS_ID` environment variable
  c. The OS-assigned process ID (existing behavior)

The resolved value is masked to 5 bits (range 0-31) as before.

All existing comments and documentation are preserved. New XML doc
comments are added for the new property and method. A new
"Process ID Configuration" section is added to the README. A new
`ProcessIdTests` test class covers all configuration paths.
Add .gitattributes to normalize line endings to LF across the
repository. This resolves the full-file diff caused by the original
CRLF line endings in Id.cs conflicting with the LF line endings
in the pushed content. Re-push Id.cs with clean LF line endings.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Process ID is possibly always 1 on Dockerized applications

1 participant