{"id":37037768,"url":"https://github.com/safakgur/snowflakes","last_synced_at":"2026-01-14T04:29:44.080Z","repository":{"id":258449352,"uuid":"850086592","full_name":"safakgur/snowflakes","owner":"safakgur","description":"Snowflake ID generator for .NET","archived":false,"fork":false,"pushed_at":"2025-12-15T07:27:07.000Z","size":127,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-18T04:52:24.563Z","etag":null,"topics":["distributed","dotnet","id-generator","library","snowflake-id","snowflake-twitter"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/safakgur.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-08-30T21:12:43.000Z","updated_at":"2025-12-15T07:27:09.000Z","dependencies_parsed_at":"2025-01-12T09:27:28.914Z","dependency_job_id":"a1dd5638-aa26-42df-a978-89e04d4065a3","html_url":"https://github.com/safakgur/snowflakes","commit_stats":null,"previous_names":["safakgur/snowflakes"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/safakgur/snowflakes","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/safakgur%2Fsnowflakes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/safakgur%2Fsnowflakes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/safakgur%2Fsnowflakes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/safakgur%2Fsnowflakes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/safakgur","download_url":"https://codeload.github.com/safakgur/snowflakes/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/safakgur%2Fsnowflakes/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28409495,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["distributed","dotnet","id-generator","library","snowflake-id","snowflake-twitter"],"created_at":"2026-01-14T04:29:43.525Z","updated_at":"2026-01-14T04:29:44.071Z","avatar_url":"https://github.com/safakgur.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Snowflakes ![Logo][logo]\n\n[![CI][wf-ci-badge]][wf-ci]\n[![CodeQL Advanced][wf-codeql-badge]][wf-codeql]\n[![NuGet][nuget-badge]][nuget]\n\nSnowflake IDs are typically 64-bit, unique, sortable identifiers that can be generated in a distributed system\nwithout a central authority. The format was [originally created by Twitter (now X)][twitter-announcement]\nand adopted by others like [Sony][sonyflake], [Discord][discord-snowflakes], [Instagram][instagram-sharding-and-ids], and more.\n\nThis .NET library lets you create customized snowflakes by configuring the components:\nTimestamp, Sequence, and Instance ID, using any integer type.\n\n## Layout\n\nTraditional snowflakes are 64-bit IDs, where only 63 bits are used for one to fit in a signed\ninteger. The library defaults to `long`/`Int64` snowflakes for maximum compatibility, but allows\nother integer types to be used when desired. There are examples of this later in this document.\n\nThere are three standard components that make up a snowflake:\n\n1. **Timestamp:** Time passed since a set epoch. Both the epoch and the unit (precision) are adjustable.\n2. **Instance ID:** Identifies the process creating the snowflake. Also known as Machine ID or Shard ID.\n3. **Sequence number:** Increments when multiple snowflakes are created in the same time unit.\n\nWhen you define the snowflake format for a system, consider the following:\n\n* **System lifetime:** Bigger timestamp length, later epoch, and lower precision mean higher lifetime.\n* **Instance count:** Bigger instance ID length means there can be more instances generating snowflakes.\n* **Generation rate:** Bigger sequence length means an instance can generate more snowflakes per time.\n* **Ordering:** Components specified earlier occupy higher bits, meaning they are prioritized during sorting.\n\n## How to Use\n\n### Configuring Snowflakes\n\nThe following examples show how you can define your own snowflake format.\n\n#### X's Implementation\n\n41-bit timestamp in milliseconds elapsed since X's epoch  \n10-bit instance ID  \n12-bit sequence number\n\n```csharp\n// X's epoch - 2010-11-04T01:42:54.657Z\nvar epoch = DateTimeOffset.FromUnixTimeMilliseconds(1288834974657);\n\n// Set the instance ID, e.g., the ordinal index of a K8 pod.\nvar instanceId = 0;\n\n// Create the generator.\nvar snowflakeGen = SnowflakeGenerator.CreateBuilder()\n    .AddTimestamp(41, epoch, TimeSpan.TicksPerMillisecond)\n    .AddConstant(10, instanceId)\n    .AddSequenceForTimestamp(12)\n    .Build();\n```\n\n#### Sony's Implementation (Sonyflake)\n\n39-bit timestamp in units of 10 ms elapsed since a custom epoch  \n8-bit sequence number  \n16-bit instance ID\n\n```csharp\n// Choose an epoch, e.g., when your system came online. Epoch can't be in the future.\nvar epoch = new DateTimeOffset(2024, 8, 30, 0, 0, 0, TimeSpan.Zero);\n\n// Set the instance ID, e.g., the ordinal index of a K8 pod.\nvar instanceId = 0;\n\n// Create the generator.\nvar snowflakeGen = SnowflakeGenerator.CreateBuilder()\n    .AddTimestamp(39, epoch, TimeSpan.TicksPerMillisecond * 10) // 10 ms increments\n    .AddSequenceForTimestamp(8)\n    .AddConstant(16, instanceId)\n    .Build();\n```\n\n#### Comparison of the Implementations\n\nFrom Sonyflake's README:\n\n* \\[Sonyflake's\\] lifetime (174 years) is longer than that of \\[X's\\] Snowflake (69 years)  \n* \\[Sonyflake\\] can work in more distributed machines (2^16) than \\[X's\\] Snowflake (2^10)  \n* \\[Sonyflake\\] can generate 2^8 IDs per 10 msec at most in a single machine/thread (slower than \\[X's\\] Snowflake)\n\nNote that X Snowflake and Sonyflake components are also placed in different orders, which means:\n\n* X Snowflake will be sorted by timestamp -\u003e instance ID -\u003e sequence number\n* Sonyflake will be sorted by timestamp -\u003e sequence number -\u003e instance ID\n\nIf you decide to use snowflakes, make sure you configure the generator based on _your system_'s requirements.\nDo not blindly copy one of the above configurations.\n\n### Generating Snowflakes\n\nOnce you have a generator, you can generate snowflakes using its `NewSnowflake` method.\n\n```csharp\nvar snowflake1 = snowflakeGen.NewSnowflake();\nvar snowflake2 = snowflakeGen.NewSnowflake();\n// ...\n```\n\nTry to keep snowflakes as integer values and use the appropriate type when persisting them to a database.\nThis will ensure they are stored efficiently and remain sortable.\n\n### Encoding Snowflakes\n\nYou can use `SnowflakeEncoder` to encode snowflakes to custom-base strings.\nThis will make them shorter, which can be useful when they are used in URIs.\n\n```csharp\n// There are base 36, 62, and 64 encoders, all URI-safe.\nvar encoder = SnowflakeEncoder.Base62Ordinal;\n\nvar snowflake = snowflakeGen.NewSnowflake(); // 139611368062976\nvar encodedSnowflake = encoder.Encode(snowflake); // \"ddw3cbIG\"\nvar decodedSnowflake = encoder.Decode(encodedSnowflake); // 139611368062976\n```\n\nMake sure you decode a snowflake back to integer before sorting or persisting it.\n\n### Dependency Injection\n\nThe `SnowflakeGenerator` instance must be shared for the generated snowflakes to be unique,\nso when using a DI container, a generator needs to be registered as a singleton.\n\n```csharp\nservices.AddSingleton(static serviceProvider =\u003e\n{\n    // A fixed epoch, specific to the system that will use the generated snowflakes.\n    // Do not change this value once you have snowflakes in the wild.\n    // The epoch must be earlier than the current time at the time of snowflake generation.\n    var epoch = new DateTimeOffset(2024, 8, 30, 0, 0, 0, TimeSpan.Zero);\n\n    // Assuming a time provider is registered.\n    // Feel free to omit it when setting up the timestamp component (Default: TimeProvider.System).\n    var timeProvider = serviceProvider.GetRequiredService\u003cTimeProvider\u003e();\n\n    // Assuming there is an options class that can provide information about the current instance.\n    // Feel free to obtain the instance ID by different means.\n    var programOpts = serviceProvider.GetRequiredService\u003cIOptions\u003cProgramOptions\u003e\u003e().Value;\n    var instanceId = programOpts.InstanceId;\n\n    // The generator below uses the Sonyflake configuration.\n    return SnowflakeGenerator.CreateBuilder()\n        .AddTimestamp(39, epoch, TimeSpan.TicksPerMillisecond * 10, timeProvider)\n        .AddSequenceForTimestamp(8)\n        .AddConstant(16, instanceId)\n        .Build();\n});\n```\n\nConsider [keyed registrations][dotnet-di-keyed] if your application needs to generate multiple types of snowflakes.\n\nOnce registered, you can inject and use the generator like any other dependency.\n\n```csharp\npublic class FooService(SnowflakeGenerator\u003clong\u003e snowflakeGen)\n{\n    public Foo CreateFoo() =\u003e new()\n    {\n        Id = snowflakeGen.NewSnowflake()\n        // ...\n    };\n}\n```\n\n### Advanced\n\n#### Custom-Size Snowflakes\n\nSnowflakes are traditionally 64-bit integers, signed in most implementations but unsigned in others\n(like Discord's). While this library defaults to `long`/`Int64` for maximum compatibility, it also\nallows you to use any integer type that satisfies the constraint,\n`where T : struct, IBinaryInteger\u003cT\u003e, IMinMaxValue\u003cT\u003e`. All common integer types in .NET (other than\n`BigInteger`, which doesn't implement `IMinMaxValue\u003cT\u003e`) fall into this group.\n\nYou can specify a custom type by using the generic `SnowflakeGenerator.Create*` overloads.\n\n```csharp\n// No generic type argument - defaults to a builder to 64-bit signed snowflakes.\nSnowflakeGenerator.CreateBuilder();\n\n// Same as above, but the type is specified explicitly.\nSnowflakeGenerator.CreateBuilder\u003clong\u003e();\n\n// Same size, but unsigned, so we get to use one extra bit.\nSnowflakeGenerator.CreateBuilder\u003culong\u003e();\n\n// Builders to generate 32-bit signed and unsigned snowflakes - half the default size.\nSnowflakeGenerator.CreateBuilder\u003cint\u003e();\nSnowflakeGenerator.CreateBuilder\u003cuint\u003e();\n\n// Builders to generate 128-bit signed and unsigned snowflakes - double the default size.\nSnowflakeGenerator.CreateBuilder\u003cInt128\u003e();\nSnowflakeGenerator.CreateBuilder\u003cUInt128\u003e();\n\n// + sbyte, byte, short, ushort, and any other binary integer implementation.\n```\n\n`SnowflakeEncoder` also supports specifying the snowflake type.\n\n```csharp\n// No generic type argument - defaults to decoding into a 64-bit signed integer.\nSnowflakeEncoder.Base62Ordinal.Decode(encodedSnowflake);\n\n// Same as above, but the type is specified explicitly.\nSnowflakeEncoder.Base62Ordinal.Decode\u003clong\u003e(encodedSnowflake);\n```\n\n#### Blocking Timestamp Generation\n\nStandard snowflakes use a sequence number to prevent collisions when multiple snowflakes are\ngenerated in the same time unit. This library provides an additional snowflake component, called\n`BlockingTimestampSnowflakeComponent`, that provides an alternative to using sequence numbers.\n\nWith blocking timestamps, calls to `NewSnowflake` will block the thread until the next timestamp\nunit is available, ensuring that no two snowflakes are generated with the same timestamp.\nThis approach eliminates the need for a sequence component, simplifying the ID generation process.\nHowever, it can potentially slow down ID generation, especially under high load, as threads may\nfrequently need to wait for the next timestamp unit to become available. On the upside, not having\nsequence numbers means more bits are available for the timestamp, allowing for smaller units.\n\n```csharp\nvar snowflakeGen = SnowflakeGenerator.CreateBuilder()\n    .AddBlockingTimestamp(44, epoch, TimeSpan.TicksPerMillisecond / 2)\n    .AddConstant(19, instanceId)\n    .Build();\n```\n\nThe example above sets the timestamp to be 44 bits with half-millisecond precision, meaning it will\nhave a 278-year lifetime and allow two snowflakes to be generated every millisecond.\n\n#### Custom Components\n\nThe library already offers the usual timestamp, instance ID, and sequence number components,\nbut it also allows you to create your own components by subclassing `SnowflakeComponent`.\n\nBelow is an example custom component that provides random bits.\n\n```csharp\npublic sealed class RandomSnowflakeComponent\u003cT\u003e : SnowflakeComponent\u003cT\u003e\n    where T : struct, IBinaryInteger\u003cT\u003e, IMinMaxValue\u003cT\u003e\n{\n    public RandomSnowflakeComponent(int lengthInBits) : base(lengthInBits)\n    {\n        AllowTruncation = true;\n    }\n\n    public override T CalculateValue(SnowflakeGenerationContext\u003cT\u003e ctx)\n    {\n        Span\u003cbyte\u003e buffer = stackalloc byte[MaxLengthInBytes];\n        RandomNumberGenerator.Fill(buffer);\n\n        return T.ReadLittleEndian(buffer, IsUnsigned);\n    }\n}\n```\n\nYou can configure a snowflake generator to use any `SnowflakeComponent` implementation.\n\n```csharp\nvar snowflakeGen = SnowflakeGenerator.CreateBuilder()\n    .AddTimestamp(30, epoch)\n    .Add(new RandomSnowflakeComponent\u003clong\u003e(33)) // Here we add our custom component\n    .Build();\n\n// High 30 bits have milliseconds elapsed since `epoch` while low 33 bits are random.\n// Similar to a version 7 UUID, albeit with a smaller range.\nvar snowflake = snowflakeGen.NewSnowflake();\n```\n\nIf you're feeling fancy, you can also write an extension method for `SnowflakeGeneratorBuilder`.\n\n```csharp\npublic static class SnowflakeGeneratorBuilderExtensions\n{\n    public static SnowflakeGeneratorBuilder\u003cT\u003e AddRandom\u003cT\u003e(\n        this SnowflakeGeneratorBuilder\u003cT\u003e builder, int lengthInBits)\n        where T : struct, IBinaryInteger\u003cT\u003e, IMinMaxValue\u003cT\u003e\n    {\n        ArgumentNullException.ThrowIfNull(builder);\n\n        return builder.Add(new RandomSnowflakeComponent\u003cT\u003e(lengthInBits));\n    }\n}\n```\n\nThe extension we created above allows usage like `AddRandom(33)`.\n\n```csharp\nvar snowflakeGen = SnowflakeGenerator.CreateBuilder()\n    .AddTimestamp(30, epoch)\n    .AddRandom(33) // Extension method\n    .Build();\n```\n\n#### Snowflake Generation in Tests\n\nYou can't mock `SnowflakeGenerator` itself as it is sealed, but you can easily create instances\nof it that return constant test data.\n\n```csharp\nvar testSnowflakeGen = SnowflakeGenerator.CreateBuilder()\n    .AddConstant(63, 123L)\n    .Build();\n\n// `testSnowflakeGen.NewSnowflake()` will always return 123.\n```\n\nIf you want your test snowflake generation to be dynamic, you can mock (or inherit from) `SnowflakeComponent`.\n\n```csharp\nvar random = new Random();\n\n// NSubstitute example\nvar testComponent = Substitute.For\u003cSnowflakeComponent\u003clong\u003e\u003e(31);\ntestComponent\n    .CalculateValue(Arg.Any\u003cSnowflakeGenerationContext\u003clong\u003e\u003e())\n    .Returns(call =\u003e\n    {\n        // Context can be used to access other components of the generator.\n        // var ctx = call.Arg\u003cSnowflakeGenerationContext\u003clong\u003e\u003e();\n        // var timestampLastValue = ctx.Components[0].LastValue;\n\n        // Return any value for tests.\n        return random.Next();\n    });\n\n// Assuming we have a test time provider\nvar timeProvider = TestTimeProvider.Frozen;\nvar testEpoch = timeProvider.GetUtcNow().AddDays(-10);\nvar testSnowflakeGen = SnowflakeGenerator.CreateBuilder()\n    .AddTimestamp(32, testEpoch, timeProvider: timeProvider)\n    .Add(testComponent)\n    .Build();\n```\n\n## Support\n\nIf you need any help, please feel free to [create an issue with the \"question\" label][issues-ask].\n\n## Contributing\n\nThank you for your interest in contributing to Snowflakes!\n\nPlease see the [CONTRIBUTING.md](CONTRIBUTING.md) for more information.\n\nFor security bugs and vulnerabilities, please see [SECURITY.md](SECURITY.md).\n\n[logo]: https://raw.githubusercontent.com/safakgur/snowflakes/main/media/logo-28.png \"Logo\"\n\n[wf-ci]: https://github.com/safakgur/snowflakes/actions/workflows/ci.yml \"CI Workflow\"\n[wf-ci-badge]: https://github.com/safakgur/snowflakes/actions/workflows/ci.yml/badge.svg?event=push \"CI Badge\"\n\n[wf-codeql]: https://github.com/safakgur/snowflakes/actions/workflows/codeql.yml\n[wf-codeql-badge]: https://github.com/safakgur/snowflakes/actions/workflows/codeql.yml/badge.svg?branch=main\u0026event=push\n\n[nuget]: https://www.nuget.org/packages/Snowflakes/ \"NuGet Gallery\"\n[nuget-badge]: https://img.shields.io/nuget/v/Snowflakes.svg?style=flat \"NuGet Badge\"\n\n[twitter-announcement]: https://blog.twitter.com/2010/announcing-snowflake \"Announcing Snowflake\"\n[sonyflake]: https://github.com/sony/sonyflake \"Sonyflake\"\n[discord-snowflakes]: https://discord.com/developers/docs/reference#snowflakes \"Discord Developer Portal\"\n[instagram-sharding-and-ids]: https://instagram-engineering.com/sharding-ids-at-instagram-1cf5a71e5a5c \"Sharding \u0026 IDs at Instagram\"\n\n[dotnet-di-keyed]: https://learn.microsoft.com/dotnet/core/extensions/dependency-injection#keyed-services \".NET Dependency Injection\"\n\n[issues-ask]: https://github.com/safakgur/snowflakes/issues/new?labels=question\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsafakgur%2Fsnowflakes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsafakgur%2Fsnowflakes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsafakgur%2Fsnowflakes/lists"}