Snowflake ID for Users

Snowflake ID's are useful for giving a practically unique ID that contains machine and time data.

Snowflake ID for Users

Users and content benefit from Snowflake ID which allow lookups of machine and time data.

Snowflake ID is a format first developed by Twitter in 2010 and has been modified and used by other platforms. It uses Unix epoch time, and machine or shards and is applied to micro-blog posts (Tweets etc), users, media etc etc.

Using Snowflake ID's assigned to content (micro-blog posts) you can lookup content before and after.

I have used the Elixir Snowflake package and applied a simple Snowflake handler for content.

defmodule Folkbot.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset
  alias Folkbot.Snowflaker

  schema "users" do
    field :admin, :boolean, default: false
    field :banner_image, :string
    field :bio, :string
    field :editor, :boolean, default: false
    field :email, :string
    field :first_name, :string
    field :host, :boolean, default: false
    field :last_name, :string
    field :location, :string
    field :profile_image, :string
    field :seller, :boolean, default: false
    field :slug, :string
    field :snowflake, :string
    field :tagline, :string
    field :username, :string
    field :website, :string

    timestamps()
  end

  @doc false
  def changeset(user, attrs) do
    user
    |> cast(attrs, [
      :username,
      :first_name,
      :last_name,
      :slug,
      :email,
      :bio,
      :location,
      :website,
      :profile_image,
      :banner_image,
      :tagline,
      :snowflake,
      :admin,
      :editor,
      :host,
      :seller])
    |> validate_required([
      :username,
      :email,
      :website,
      :admin,
      :editor,
      :host,
      :seller])
    |> snowflake_for_new_user()
  end

  def snowflake_for_new_user(changeset) do
    Snowflaker.snowflake_for_new_content(changeset)
  end

end

The Snowflaker module

defmodule Folkbot.Snowflaker do
  import Ecto.Changeset
  @moduledoc false

  def next_snowflake() do
    {:ok, snowflake} = Snowflake.next_id()
    to_string(snowflake)
  end

  def snowflake_for_new_content(changeset) do
    snowflake = next_snowflake()
    put_change(changeset, :snowflake, snowflake)
  end

end

Now whenever a user is created, a Snowflake ID is also created.

I have created a helper script lib/folkbot_web/scripts/user.iex.exs

import Ecto.Query
import Plug.Conn
use Ecto.Schema
import Ecto.Changeset
alias Folkbot.Accounts
alias Folkbot.Accounts.User

time = DateTime.now!("Europe/Copenhagen")
random = Enum.random(0..100000)
user_params = %{
  username: "niccolox#{random}}",
  website: "devekko.com",
  email: "niccolox#{random}@devekko.com",
  admin: true,
  editor: true,
  seller: true,
  host: true
}
Accounts.create_user(user_params)

Gives us

iex(9)> random = Enum.random(0..100000)
3772
iex(10)> user_params = %{
...(10)>   username: "niccolox#{random}}",
...(10)>   website: "devekko.com",
...(10)>   email: "niccolox#{random}@devekko.com",
...(10)>   admin: true,
...(10)>   editor: true,
...(10)>   seller: true,
...(10)>   host: true
...(10)> }
%{
  admin: true,
  editor: true,
  email: "[email protected]",
  host: true,
  seller: true,
  username: "niccolox3772}",
  website: "devekko.com"
}
iex(11)> Accounts.create_user(user_params)
[debug] QUERY OK db=19.4ms decode=2.2ms queue=0.8ms idle=851.0ms
INSERT INTO "users" ("admin","editor","email","host","seller","snowflake","username","website","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) RETURNING "id" [true, true, "[email protected]", true, true, "6831485532917207040", "niccolox3772}", "devekko.com", ~N[2021-08-31 04:16:13], ~N[2021-08-31 04:16:13]]
{:ok,
 %Folkbot.Accounts.User{
   __meta__: #Ecto.Schema.Metadata<:loaded, "users">,
   admin: true,
   banner_image: nil,
   bio: nil,
   editor: true,
   email: "[email protected]",
   first_name: nil,
   host: true,
   id: 2,
   inserted_at: ~N[2021-08-31 04:16:13],
   last_name: nil,
   location: nil,
   profile_image: nil,
   seller: true,
   slug: nil,
   snowflake: "6831485532917207040",
   tagline: nil,
   updated_at: ~N[2021-08-31 04:16:13],
   username: "niccolox3772}",
   website: "devekko.com"
 }}

Snowflake functions can work on Snowflake ID's https://hexdocs.pm/snowflake/

Snowflake.Helper.epoch()
1630299375
iex(2)> Snowflake.Helper.machine_id()
1
iex(3)> Snowflake.Util.timestamp_of_id(6831482055310708736)
1628752244785
iex(4)> Snowflake.Util.real_timestamp_of_id(6831482055310708736)
1630382544160
iex(14)> Snowflake.Util.real_timestamp_of_id(6831482055310708736) |> DateTime.from_unix!(:millisecond)
~U[2021-08-31 04:02:24.160Z]

iex(31)> alias Folkbot.Snowflaker                                      
Folkbot.Snowflaker
iex(32)> snowflakeid = Snowflaker.next_snowflake() |> String.to_integer
6831521411299807232
iex(33)> Snowflake.Util.real_timestamp_of_id(snowflakeid) |> DateTime.from_unix!(:millisecond)
~U[2021-08-31 06:38:47.359Z]

Git commit: https://github.com/FolkBot/folkbot/commit/3d5ad7bef0253f14d3a78a5eebda093f97a9ce16

Not open today, try again tomorrow