Home > Software design >  Time nonce generation in go routines
Time nonce generation in go routines

Time:12-05

I am calling rest api which expects nonce header. The nonce must be unique timestamp and every consecutive call should have timestamp > previous one. My goal is to launch 10 go routines and from each one do a call to the web api. Since we do not have control over the routine execution order we might end up doing a webapi call with a nonce < previous one. I do not have control over the api implementation.

I have stripped down my code to something very simple which illustrate the problem:

package main

import (
    "fmt"
    "time"
)

func main() {
    count := 10
    results := make(chan string, count)

    for i := 0; i < 10; i   {
        go someWork(results)
        // Enabling the following line would give the 
        // expected outcome but does look like a hack to me.
        // time.Sleep(time.Millisecond)
    }

    for i := 0; i < count; i   {
        fmt.Println(<-results)
    }
}

func someWork(done chan string) {
    // prepare http request, do http request, send to done chan the result
    done <- time.Now().Format("15:04:05.00000")
}

From the output you can see how we have timestamps which are not chronologically ordered:

13:18:26.98549
13:18:26.98560
13:18:26.98561
13:18:26.98553
13:18:26.98556
13:18:26.98556
13:18:26.98557
13:18:26.98558
13:18:26.98559
13:18:26.98555

What would be the idiomatic way to achieve the expected outcome without adding the sleep line?

Thanks!

CodePudding user response:

I think: you can use a WaitGroup, for example:

package main

import (
    "fmt"
    "sync"
    "time"
)

var wg sync.WaitGroup = sync.WaitGroup{}
var ct int = 0

func hello() {
    fmt.Printf("Hello Go %v\n", time.Now().Format("15:04:05.00000"))
    // when you are done, call done:
    time.Sleep(time.Duration(10 * int(time.Second)))
    wg.Done()
}

func main() {
    for i := 0; i < 10; i   {
        wg.Add(1)
        go hello()
        wg.Wait()
    }

}

CodePudding user response:

As I understand you only need to synchronize (serialize) the goroutines till request send part, that is where the timestamp and nonce need to be sequential. response processing can be parallely

You can use a mutex for this case like in below code

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    count := 10
    results := make(chan string, count)
    var mutex sync.Mutex
    for i := 0; i < count; i   {
        go someWork(&mutex, results)
    }

    for i := 0; i < count; i   {
        fmt.Println(<-results)
    }
}

func someWork(mut *sync.Mutex, done chan string) {
    // Lock the mutex, go routine getting lock here, 
    // is guarranteed to create the timestamp and 
    // perform the request before any other
    mut.Lock()
    // Get the timestamp
    myTimeStamp := time.Now().Format("15:04:05.00000")
    //prepare http request, do http request
    //free the mutex
    mut.Unlock()

    // Process response
    // send to done chan the result
    done <- myTimeStamp
}

Output is chronolgically ordered

21:24:03.36582
21:24:03.36593
21:24:03.36595
21:24:03.36596
21:24:03.36597
21:24:03.36597
21:24:03.36598
21:24:03.36598
21:24:03.36599
21:24:03.36600

But still some duplicate timestamps, may be need more finegrained timestamp, but that is up to the use case.

Hope this helps.

  • Related