Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce New function #47

Merged
merged 7 commits into from
Aug 13, 2023
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ go get github.com/sony/sonyflake
Usage
-----

The function NewSonyflake creates a new Sonyflake instance.
The function New creates a new Sonyflake instance.

```go
func NewSonyflake(st Settings) *Sonyflake
func New(st Settings) (*Sonyflake, error)
```

You can configure Sonyflake by the struct Settings:
Expand Down
42 changes: 32 additions & 10 deletions sonyflake.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,29 @@ type Sonyflake struct {
machineID uint16
}

var (
ErrStartTimeAhead = errors.New("start time is ahead of now")
ErrNoPrivateAddress = errors.New("no private ip address")
ErrOverTimeLimit = errors.New("over the time limit")
ErrInvalidMachineID = errors.New("invalid machine id")
)

var defaultInterfaceAddrs = net.InterfaceAddrs

// NewSonyflake returns a new Sonyflake configured with the given Settings.
// NewSonyflake returns nil in the following cases:
// New returns a new Sonyflake configured with the given Settings.
// New returns an error in the following cases:
// - Settings.StartTime is ahead of the current time.
// - Settings.MachineID returns an error.
// - Settings.CheckMachineID returns false.
func NewSonyflake(st Settings) *Sonyflake {
func New(st Settings) (*Sonyflake, error) {
if st.StartTime.After(time.Now()) {
return nil, ErrStartTimeAhead
}

sf := new(Sonyflake)
sf.mutex = new(sync.Mutex)
sf.sequence = uint16(1<<BitLenSequence - 1)

if st.StartTime.After(time.Now()) {
return nil
}
if st.StartTime.IsZero() {
sf.startTime = toSonyflakeTime(time.Date(2014, 9, 1, 0, 0, 0, 0, time.UTC))
} else {
Expand All @@ -79,10 +87,24 @@ func NewSonyflake(st Settings) *Sonyflake {
} else {
sf.machineID, err = st.MachineID()
}
if err != nil || (st.CheckMachineID != nil && !st.CheckMachineID(sf.machineID)) {
return nil
if err != nil {
return nil, err
}

if st.CheckMachineID != nil && !st.CheckMachineID(sf.machineID) {
return nil, ErrInvalidMachineID
}

return sf, nil
}

// NewSonyflake returns a new Sonyflake configured with the given Settings.
// NewSonyflake returns nil in the following cases:
// - Settings.StartTime is ahead of the current time.
// - Settings.MachineID returns an error.
// - Settings.CheckMachineID returns false.
func NewSonyflake(st Settings) *Sonyflake {
sf, _ := New(st)
return sf
}

Expand Down Expand Up @@ -127,7 +149,7 @@ func sleepTime(overtime int64) time.Duration {

func (sf *Sonyflake) toID() (uint64, error) {
if sf.elapsedTime >= 1<<BitLenTime {
return 0, errors.New("over the time limit")
return 0, ErrOverTimeLimit
}

return uint64(sf.elapsedTime)<<(BitLenSequence+BitLenMachineID) |
Expand All @@ -152,7 +174,7 @@ func privateIPv4(interfaceAddrs types.InterfaceAddrs) (net.IP, error) {
return ip, nil
}
}
return nil, errors.New("no private ip address")
return nil, ErrNoPrivateAddress
}

func isPrivateIPv4(ip net.IP) bool {
Expand Down
79 changes: 55 additions & 24 deletions sonyflake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sonyflake

import (
"bytes"
"errors"
"fmt"
"net"
"runtime"
Expand Down Expand Up @@ -40,6 +41,60 @@ func nextID(t *testing.T) uint64 {
return id
}

func TestNew(t *testing.T) {
genError := fmt.Errorf("an error occurred while generating ID")

tests := []struct {
name string
settings Settings
err error
}{
{
name: "failure: time ahead",
settings: Settings{
StartTime: time.Now().Add(time.Minute),
},
err: ErrStartTimeAhead,
},
{
name: "failure: machine ID",
settings: Settings{
MachineID: func() (uint16, error) {
return 0, genError
},
},
err: genError,
},
{
name: "failure: invalid machine ID",
settings: Settings{
CheckMachineID: func(uint16) bool {
return false
},
},
err: ErrInvalidMachineID,
},
{
name: "success",
settings: Settings{},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
sonyflake, err := New(test.settings)

if !errors.Is(err, test.err) {
t.Fatalf("unexpected value, want %#v, got %#v", test.err, err)
}

if sonyflake == nil && err == nil {
t.Fatal("unexpected value, sonyflake should not be nil")
}
})
}
}

func TestSonyflakeOnce(t *testing.T) {
sleepTime := time.Duration(50 * sonyflakeTimeUnit)
time.Sleep(sleepTime)
Expand Down Expand Up @@ -150,30 +205,6 @@ func TestSonyflakeInParallel(t *testing.T) {
fmt.Println("number of id:", len(set))
}

func TestNilSonyflake(t *testing.T) {
var startInFuture Settings
startInFuture.StartTime = time.Now().Add(time.Duration(1) * time.Minute)
if NewSonyflake(startInFuture) != nil {
t.Errorf("sonyflake starting in the future")
}

var noMachineID Settings
noMachineID.MachineID = func() (uint16, error) {
return 0, fmt.Errorf("no machine id")
}
if NewSonyflake(noMachineID) != nil {
t.Errorf("sonyflake with no machine id")
}

var invalidMachineID Settings
invalidMachineID.CheckMachineID = func(uint16) bool {
return false
}
if NewSonyflake(invalidMachineID) != nil {
t.Errorf("sonyflake with invalid machine id")
}
}

func pseudoSleep(period time.Duration) {
sf.startTime -= int64(period) / sonyflakeTimeUnit
}
Expand Down
Loading