Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github: [kkrypt0nn]
custom: ["https://buymeacoffee.com/kkrypt0nn"]
49 changes: 49 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Spaceflake CI (Lint & Test)

on:
push:
branches:
- main
pull_request:
workflow_dispatch:

jobs:
lint-test:
name: Lint & Test
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.24
- name: Lint
uses: golangci/golangci-lint-action@v7
with:
version: latest
- name: Test
run: go test -v ./...
build:
name: Build for ${{ matrix.target.goos }}/${{ matrix.target.goarch }}
runs-on: ubuntu-latest
needs: [lint-test]
strategy:
matrix:
target:
- { goos: linux, goarch: amd64 }
- { goos: linux, goarch: arm64 }
- { goos: darwin, goarch: amd64 }
- { goos: darwin, goarch: arm64 }
- { goos: windows, goarch: amd64 }
- { goos: windows, goarch: arm64 }
- { goos: android, goarch: arm64 }
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.24
- name: Build
run: GOOS=${{ matrix.target.goos }} GOARCH=${{ matrix.target.goarch }} go build -v ./...
23 changes: 0 additions & 23 deletions .github/workflows/go-test.yml

This file was deleted.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/kkrypt0nn/spaceflake

go 1.19
go 1.24
24 changes: 21 additions & 3 deletions spaceflake.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const (
MAX12BITS = 4095
// MAX41BITS is the maximum value for a 41 bits number
MAX41BITS = 2199023255551
// CLOCK_DRIFT_TOLERANCE_MS is the tolerance for clock drift in milliseconds
CLOCK_DRIFT_TOLERANCE_MS = 10
)

// Spaceflake represents a Spaceflake
Expand Down Expand Up @@ -162,13 +164,15 @@ type Worker struct {
Sequence uint64
// ID is the worker ID that the Spaceflake generator will use for the next 5 bits
ID uint64

increment uint64
mutex *sync.Mutex
// used to prevent clockdrift
lastTimestamp uint64
increment uint64
mutex *sync.Mutex
}

// GenerateSpaceflake generates a Spaceflake
func (w *Worker) GenerateSpaceflake() (*Spaceflake, error) {

if w.Node == nil {
return nil, fmt.Errorf("node is not set")
}
Expand Down Expand Up @@ -197,6 +201,16 @@ func (w *Worker) GenerateSpaceflake() (*Spaceflake, error) {
milliseconds := uint64(math.Floor(microTime() * 1000))
milliseconds -= w.BaseEpoch

if delta := w.lastTimestamp - milliseconds; milliseconds < w.lastTimestamp {
if delta >= CLOCK_DRIFT_TOLERANCE_MS {
return nil, fmt.Errorf("clock moved backwards by %dms", delta)
}
time.Sleep(time.Duration(delta+1) * time.Millisecond)
milliseconds = uint64(math.Floor(microTime()*100)) - w.BaseEpoch
}

w.lastTimestamp = milliseconds

base := stringPadLeft(decimalBinary(milliseconds), 41, "0")
nodeID := stringPadLeft(decimalBinary(w.Node.ID), 5, "0")
workerID := stringPadLeft(decimalBinary(w.ID), 5, "0")
Expand Down Expand Up @@ -250,6 +264,10 @@ func (w *Worker) GenerateSpaceflakeAt(at time.Time) (*Spaceflake, error) {
milliseconds := uint64(math.Floor(microTime * 1000))
milliseconds -= w.BaseEpoch

if milliseconds < w.lastTimestamp {
return nil, fmt.Errorf("cannot generate Spaceflake: Detected clock drift. The time you want to generate the Spaceflake at is before the last generated Spaceflake time")
}

base := stringPadLeft(decimalBinary(milliseconds), 41, "0")
nodeID := stringPadLeft(decimalBinary(w.Node.ID), 5, "0")
workerID := stringPadLeft(decimalBinary(w.ID), 5, "0")
Expand Down
22 changes: 0 additions & 22 deletions spaceflake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,28 +160,6 @@ func TestSpaceflakeWorkerGoroutineUnique(t *testing.T) {
t.Log("Success! All Spaceflakes are unique")
}

func TestSameTimeStampDifferentBaseEpoch(t *testing.T) {
node := NewNode(1)
worker := node.NewWorker()
sf1, err := worker.GenerateSpaceflake() // Default epoch
if err != nil {
t.Error(err)
return
}
worker.BaseEpoch = 1640995200000 // Saturday, January 1, 2022 12:00:00 AM GMT
sf2, err := worker.GenerateSpaceflake()
if err != nil {
t.Error(err)
return
}
if sf1.Time() == sf2.Time() {
t.Log("Success! Generated same timestamp for different base epoch")
return
}

t.Error("Failed! Generated different timestamps for different base epoch")
}

func TestSpaceflakeGenerateUnique(t *testing.T) {
spaceflakes := map[uint64]*Spaceflake{}
settings := NewGeneratorSettings()
Expand Down
Loading