Skip to content

Commit

Permalink
Merge pull request #9 from configcat/data-governance
Browse files Browse the repository at this point in the history
  • Loading branch information
z4kn4fein authored Oct 12, 2020
2 parents 4c4f760 + 9d1f857 commit f191751
Show file tree
Hide file tree
Showing 25 changed files with 585 additions and 168 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ go:
- master

before_script:
- cd v5
- cd v6
- go vet ./...

script:
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2018 ConfigCat
Copyright (c) 2020 ConfigCat

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ ConfigCat is a <a target="_blank" href="https://configcat.com">hosted feature fl

### 1. Install the package with `go`
```bash
go get github.com/configcat/go-sdk/v5
go get github.com/configcat/go-sdk/v6
```

### 2. Go to <a href="https://app.configcat.com/sdkkey" target="_blank">Connect your application</a> tab to get your *SDK Key*:
Expand All @@ -26,7 +26,7 @@ go get github.com/configcat/go-sdk/v5

### 3. Import the *ConfigCat* client package to your application
```go
import "github.com/configcat/go-sdk/v5"
import "github.com/configcat/go-sdk/v6"
```

### 4. Create a *ConfigCat* client instance:
Expand Down
4 changes: 2 additions & 2 deletions v6/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ ConfigCat is a <a target="_blank" href="https://configcat.com">hosted feature fl

### 1. Install the package with `go`
```bash
go get github.com/configcat/go-sdk/v5
go get github.com/configcat/go-sdk/v6
```

### 2. Go to <a href="https://app.configcat.com/sdkkey" target="_blank">Connect your application</a> tab to get your *SDK Key*:
Expand All @@ -26,7 +26,7 @@ go get github.com/configcat/go-sdk/v5

### 3. Import the *ConfigCat* client package to your application
```go
import "github.com/configcat/go-sdk/v5"
import "github.com/configcat/go-sdk/v6"
```

### 4. Create a *ConfigCat* client instance:
Expand Down
47 changes: 28 additions & 19 deletions v6/async_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package configcat

import (
"errors"
"sync"
"sync/atomic"
"time"
)

Expand All @@ -19,17 +17,13 @@ import (
// })
// go func() { async.Complete("success") }()
type asyncResult struct {
state uint32
completions []func(result interface{})
done chan struct{}
result interface{}
result interface{}
*async
sync.RWMutex
}

// newAsyncResult initializes a new async object with result.
func newAsyncResult() *asyncResult {
return &asyncResult{state: pending, completions: []func(result interface{}){}, done: make(chan struct{}), async: newAsync()}
return &asyncResult{async: newAsync()}
}

// asCompletedAsyncResult creates an already completed async object.
Expand Down Expand Up @@ -67,20 +61,35 @@ func (asyncResult *asyncResult) applyThen(completion func(result interface{}) in
return newAsyncResult
}

// compose allows the chaining of the async operations after each other and subscribes a
// callback function which gets the operation result as argument and returns a new async object.
// Returns an AsyncResult object which returns a different result type.
// For example:
// async.compose(func(result interface{}) {
// newAsyncResult := newAsyncResult()
//
// DoSomethingAsynchronously(func(result interface{}) {
// newAsyncResult.complete(result)
// }))
//
// return newAsyncResult
// })
func (asyncResult *asyncResult) compose(completion func(result interface{}) *asyncResult) *asyncResult {
newAsyncResult := newAsyncResult()
asyncResult.accept(func(result interface{}) {
newResult := completion(result)
newResult.accept(func(result interface{}) {
newAsyncResult.complete(result)
})
})
return newAsyncResult
}

// complete moves the async operation into the completed state.
// Gets the result of the operation as argument.
func (asyncResult *asyncResult) complete(result interface{}) {
if atomic.CompareAndSwapUint32(&asyncResult.state, pending, completed) {
asyncResult.result = result
asyncResult.async.complete()
close(asyncResult.done)
asyncResult.RLock()
defer asyncResult.RUnlock()
for _, comp := range asyncResult.completions {
comp(result)
}
}
asyncResult.completions = nil
asyncResult.result = result
asyncResult.async.complete()
}

// get blocks until the async operation is completed,
Expand Down
13 changes: 7 additions & 6 deletions v6/auto_polling_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,12 @@ func AutoPollWithChangeListener(
// newAutoPollingPolicy initializes a new autoPollingPolicy.
func newAutoPollingPolicy(
configFetcher configProvider,
store *configStore,
cache ConfigCache,
logger Logger,
sdkKey string,
autoPollConfig autoPollConfig) *autoPollingPolicy {
policy := &autoPollingPolicy{
configRefresher: configRefresher{configFetcher: configFetcher, store: store, logger: logger},
configRefresher: newConfigRefresher(configFetcher, cache, logger, sdkKey),
autoPollInterval: autoPollConfig.autoPollInterval,
init: newAsync(),
initialized: no,
Expand All @@ -69,7 +70,7 @@ func (policy *autoPollingPolicy) getConfigurationAsync() *asyncResult {
}

return policy.init.apply(func() interface{} {
return policy.store.get()
return policy.get()
})
}

Expand Down Expand Up @@ -103,9 +104,9 @@ func (policy *autoPollingPolicy) startPolling() {
func (policy *autoPollingPolicy) poll() {
policy.logger.Debugln("Polling the latest configuration.")
response := policy.configFetcher.getConfigurationAsync().get().(fetchResponse)
cached := policy.store.get()
cached := policy.get()
if response.isFetched() && cached != response.body {
policy.store.set(response.body)
policy.set(response.body)
if policy.configChanged != nil {
policy.configChanged()
}
Expand All @@ -118,5 +119,5 @@ func (policy *autoPollingPolicy) poll() {

func (policy *autoPollingPolicy) readCache() *asyncResult {
policy.logger.Debugln("Reading from cache.")
return asCompletedAsyncResult(policy.store.get())
return asCompletedAsyncResult(policy.get())
}
9 changes: 6 additions & 3 deletions v6/auto_polling_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ func TestAutoPollingPolicy_GetConfigurationAsync(t *testing.T) {
logger := DefaultLogger(LogLevelWarn)
policy := newAutoPollingPolicy(
fetcher,
newConfigStore(logger, newInMemoryConfigCache()),
newInMemoryConfigCache(),
logger,
"",
autoPollConfig{time.Second * 2, nil},
)
defer policy.close()
Expand Down Expand Up @@ -46,8 +47,9 @@ func TestAutoPollingPolicy_GetConfigurationAsync_Fail(t *testing.T) {
logger := DefaultLogger(LogLevelWarn)
policy := newAutoPollingPolicy(
fetcher,
newConfigStore(logger, newInMemoryConfigCache()),
newInMemoryConfigCache(),
logger,
"",
autoPollConfig{time.Second * 2, nil},
)
defer policy.close()
Expand All @@ -67,8 +69,9 @@ func TestAutoPollingPolicy_GetConfigurationAsync_WithListener(t *testing.T) {
defer close(c)
policy := newAutoPollingPolicy(
fetcher,
newConfigStore(logger, newInMemoryConfigCache()),
newInMemoryConfigCache(),
logger,
"",
AutoPollWithChangeListener(
time.Second*2,
func() { c <- true },
Expand Down
56 changes: 8 additions & 48 deletions v6/config_cache.go
Original file line number Diff line number Diff line change
@@ -1,69 +1,29 @@
package configcat

import (
"sync"
)

// ConfigCache is a cache API used to make custom cache implementations.
type ConfigCache interface {
// get reads the configuration from the cache.
Get() (string, error)
Get(key string) (string, error)
// set writes the configuration into the cache.
Set(value string) error
Set(key string, value string) error
}

type inMemoryConfigCache struct {
value string
}

// configStore is used to maintain the cached configuration.
type configStore struct {
cache ConfigCache
logger Logger
inMemoryValue string
sync.RWMutex
}

func newConfigStore(log Logger, cache ConfigCache) *configStore {
return &configStore{cache: cache, logger: log}
store map[string]string
}

// newInMemoryConfigCache creates an in-memory cache implementation used to store the fetched configurations.
func newInMemoryConfigCache() *inMemoryConfigCache {
return &inMemoryConfigCache{value: ""}
return &inMemoryConfigCache{store: make(map[string]string)}
}

// get reads the configuration from the cache.
func (cache *inMemoryConfigCache) Get() (string, error) {
return cache.value, nil
func (cache *inMemoryConfigCache) Get(key string) (string, error) {
return cache.store[key], nil
}

// set writes the configuration into the cache.
func (cache *inMemoryConfigCache) Set(value string) error {
cache.value = value
func (cache *inMemoryConfigCache) Set(key string, value string) error {
cache.store[key] = value
return nil
}

// get reads the configuration.
func (store *configStore) get() string {
store.RLock()
defer store.RUnlock()
value, err := store.cache.Get()
if err != nil {
store.logger.Errorf("Reading from the cache failed, %s", err)
return store.inMemoryValue
}

return value
}

// set writes the configuration.
func (store *configStore) set(value string) {
store.Lock()
defer store.Unlock()
store.inMemoryValue = value
err := store.cache.Set(value)
if err != nil {
store.logger.Errorf("Saving into the cache failed, %s", err)
}
}
Loading

0 comments on commit f191751

Please sign in to comment.