AmitKumarDas / fun-with-programming

ABC - Always Be Coding
2 stars 2 forks source link

go code 0001 #89

Closed AmitKumarDas closed 2 years ago

AmitKumarDas commented 3 years ago
// https://github.com/emperror/errors/blob/master/error_details.go
// til // typecast // box // unbox // design // fellow
// error // wrap // cause // unwrap // stack // format // trace // stacktrace
// pure function exposes structures and methods // idiomatic design

// GetDetails extracts the key-value pairs from err's chain.
func GetDetails(err error) []interface{} {
    var details []interface{}

    // Usually there is only one error with details (when using the WithDetails API),
    // but errors themselves can also implement the details interface exposing their attributes.
    UnwrapEach(err, func(err error) bool {
        if derr, ok := err.(interface{ Details() []interface{} }); ok { // TIL
            details = append(derr.Details(), details...)
        }

        return true
    })

    return details
}

// withDetails annotates an error with arbitrary key-value pairs.
type withDetails struct {
    error   error
    details []interface{}
}

func (w *withDetails) Error() string { return w.error.Error() }
func (w *withDetails) Cause() error  { return w.error }
func (w *withDetails) Unwrap() error { return w.error }

// Details returns the appended details.
func (w *withDetails) Details() []interface{} {
    return w.details
}
AmitKumarDas commented 3 years ago
// cache // naive caching
// https://blog.chuie.io/posts/synconce/

type QueryClient struct {
    cache map[string][]byte
    mutex *sync.Mutex
}

// below still has race condition
// race //bug

func (c *QueryClient) DoQuery(name string) []byte {
    // Check if the result is already cached.
    c.mutex.Lock()
    if cached, found := c.cache[name]; found {
        c.mutex.Unlock()
        return cached, nil
    }
    c.mutex.Unlock()

    // Make the request if it's uncached.
    resp, err := http.Get("https://upstream.api/?query=" + url.QueryEscape(name))
    // Error handling and resp.Body.Close omitted for brevity.
    result, err := ioutil.ReadAll(resp)

    // Store the result in the cache.
    c.mutex.Lock()
    c.cache[name] = result
    c.mutex.Unlock()

    return result
}
AmitKumarDas commented 3 years ago
// cache // sync // once

type CacheEntry struct {
    data []byte
    once *sync.Once
}

type QueryClient struct {
    cache map[string]*CacheEntry
    mutex *sync.Mutex
}

func (c *QueryClient) DoQuery(name string) []byte {
    c.mutex.Lock()
    entry, found := c.cache[name]
    if !found {
        // Create a new entry if one does not exist already.
        entry = &CacheEntry{
            once: new(sync.Once),
        }
        c.cache[name] = entry
    }
    c.mutex.Unlock()

    // Now when we invoke `.Do`, if there is an on-going simultaneous operation,
    // it will block until it has completed (and `entry.data` is populated).
    // Or if the operation has already completed once before,
    // this call is a no-op and doesn't block.
    entry.once.Do(func() {
        resp, err := http.Get("https://upstream.api/?query=" + url.QueryEscape(name))
        // Error handling and resp.Body.Close omitted for brevity
        entry.data, err = ioutil.ReadAll(resp)
    })

    return entry.data
}