Skip to content

Instantly share code, notes, and snippets.

@tigerwill90
Last active April 30, 2025 14:04
Show Gist options
  • Select an option

  • Save tigerwill90/4188ebe0875f361655b1b8e0e1a0837b to your computer and use it in GitHub Desktop.

Select an option

Save tigerwill90/4188ebe0875f361655b1b8e0e1a0837b to your computer and use it in GitHub Desktop.
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"tailscale.com/util/singleflight"
"time"
)
// In practice, it should be thread safe.
type InMemoryCache map[string]string
func (c InMemoryCache) Get(key string) (string, error) {
v, ok := c[key]
if !ok {
return "", errors.New("key not found")
}
return v, nil
}
func (c InMemoryCache) Put(key, value string) error {
c[key] = value
return nil
}
func main() {
getter := &ScidGetter{
cache: make(InMemoryCache),
client: &http.Client{
Timeout: 5 * time.Second,
},
}
// Set a tight timeout for the overall operation
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
start := time.Now()
scid := getter.GetScid(ctx, "my-id")
fmt.Println(time.Since(start), scid)
}
type Cache interface {
Get(key string) (value string, err error)
Put(key, value string) error
}
// ScidGetter handles retrieval of SCIDs with caching and request deduplication.
type ScidGetter struct {
cache Cache
client *http.Client
group singleflight.Group[string, string]
}
func (g *ScidGetter) GetScid(ctx context.Context, idCrea string) string {
c := make(chan string)
go func() {
defer close(c)
type schema struct {
Scid string `json:"scid"`
}
// Use singleflight to deduplicate concurrent requests for the same idCrea
scid, err, _ := g.group.Do(idCrea, func() (string, error) {
// Try to get from cache first
val, err := g.cache.Get(idCrea)
if err == nil {
return val, nil
}
// Cache miss - fetch from API
resp, err := g.client.Get("https://foxmock.prod-pub.athena.tf1.fr/sylvain/scid?id_crea=" + idCrea)
if err != nil {
return "", err
}
defer resp.Body.Close()
var dto schema
if err := json.NewDecoder(resp.Body).Decode(&dto); err != nil {
return "", err
}
if err := g.cache.Put(idCrea, dto.Scid); err != nil {
log.Println(err)
}
return dto.Scid, nil
})
if err != nil {
log.Println(err)
return
}
select {
case c <- scid:
default:
}
}()
// Wait for either the context to time out or the SCID to be retrieved
select {
case <-ctx.Done():
case val := <-c:
return val
}
return ""
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment