Skip to content

Unique ID Generation#

Info

This example is for Challenge #2: Unique ID Generation

In this challenge we generate globally-unique IDs in a distributed network

Imports
import com.bilalfazlani.zioMaelstrom.*
import zio.json.*
import zio.*

Here, we define the protocol of the node. It includes messages which the node can handle and messages it can send

Message definitions
case class Generate() derives JsonDecoder
case class GenerateOk(id: String) derives JsonEncoder

Unlike echo, this node has some state which we have modeled using Ref[Int]. We increment the Int every time we generate a new Id. To make it unique across the cluster, we append the node Id to the generated id.

Why use a Ref?

Using a Ref ensures that I can update the state in a thread-safe manner. This is important because the messages are received and processed concurrently

Node application
object Main extends MaelstromNode {
  val program = receive[Generate] { _ =>
    for {
      generated <- ZIO.serviceWithZIO[Ref[Int]](_.getAndIncrement)
      me        <- MaelstromRuntime.me                
      combinedId = s"${me}_${generated}"
      _         <- reply(GenerateOk(id = combinedId)) 
    } yield ()
  }.provideSome[MaelstromRuntime](ZLayer(Ref.make(0)))
}

Tip

  • ZLayer is used to inject the state
  • .provideSome is used to provide Ref[Int] layer to the program. This method provides all the layers except MaelstromRuntime

Note

Source code for this example can be found on Github