Last active
April 30, 2025 14:04
-
-
Save tigerwill90/4188ebe0875f361655b1b8e0e1a0837b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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