Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prepare 4.9.0 release #7

Merged
merged 286 commits into from
Jul 23, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
286 commits
Select commit Hold shift + click to select a range
442088f
use an interface for options in case we ever want one that has parame…
eli-darkly Aug 22, 2018
a5a8b28
add String() method for reasons
eli-darkly Aug 22, 2018
9e009bd
add evaluation reasons to AllFlagsState; add tests for reason seriali…
eli-darkly Aug 22, 2018
408264b
misc cleanup
eli-darkly Aug 22, 2018
9663235
Merge pull request #100 from launchdarkly/eb/ch19976/explanations
eli-darkly Aug 22, 2018
359fa58
Merge branch 'all-flags-state' into explanations
eli-darkly Aug 22, 2018
52ba175
Merge pull request #103 from launchdarkly/eb/ch12124/client-side-filter
eli-darkly Aug 22, 2018
8f7bd3b
Merge branch 'all-flags-state' into explanations
eli-darkly Aug 22, 2018
41a3cf1
Merge branch 'all-flags-state' into eb/ch19976/all-flags-state-reasons
eli-darkly Aug 22, 2018
1f38723
use polymorphism for evaluation reasons
eli-darkly Aug 22, 2018
b66904b
linter
eli-darkly Aug 22, 2018
1c7cdf2
comment edits
eli-darkly Aug 22, 2018
d95fe16
use an interface for getting old-style explanations
eli-darkly Aug 23, 2018
9daa5e1
test String()
eli-darkly Aug 23, 2018
736353b
Merge branch 'eb/ch19976/all-flags-state-reasons' into eb/reason-poly…
eli-darkly Aug 23, 2018
0a3f4af
don't keep evaluating prerequisites if one fails
eli-darkly Aug 23, 2018
36598ae
Merge pull request #104 from launchdarkly/eb/ch19976/all-flags-state-…
eli-darkly Aug 23, 2018
aa4a06d
Merge pull request #105 from launchdarkly/eb/reason-polymorphism
eli-darkly Aug 23, 2018
f51ca65
Merge pull request #106 from launchdarkly/eb/single-prereq-failure
eli-darkly Aug 23, 2018
d66a080
ensure that reasons can appear in event output + fix reason field types
eli-darkly Aug 23, 2018
f3a6414
Merge pull request #107 from launchdarkly/eb/event-reasons
eli-darkly Aug 23, 2018
3cdc337
fix logic for returning default value
eli-darkly Aug 23, 2018
5cd8453
add assertion
eli-darkly Aug 24, 2018
c4858c5
Merge pull request #108 from launchdarkly/eb/default-value
eli-darkly Aug 24, 2018
735c40a
allow JSON unmarshaling of reasons inside events
eli-darkly Aug 27, 2018
9e55c35
typo in comment
eli-darkly Aug 27, 2018
6f7ed51
Merge pull request #109 from launchdarkly/eb/unmarshal-reasons
eli-darkly Aug 27, 2018
c292e11
Merge branch 'v4' of github.com:launchdarkly/go-client into v4
eli-darkly Aug 27, 2018
a6ef2ca
generate event for prerequisite even if it's off
eli-darkly Aug 30, 2018
5b39c10
Merge pull request #110 from launchdarkly/eb/ch22995/prereq-event-value
eli-darkly Aug 30, 2018
8c74ad8
Update golangci-lint to include prealloc linter (#111)
ashanbrown Sep 18, 2018
4ac5993
Spke Consul feature store
jkodumal Sep 26, 2018
6788729
Fix errant logging message referring to Redis
jkodumal Sep 26, 2018
f908ff7
Add comments
jkodumal Sep 26, 2018
16be6b7
add option to reduce front-end metadata for untracked flags
eli-darkly Oct 5, 2018
15fd0b5
Remove docs about building without redis (#114)
ashanbrown Oct 8, 2018
cf3793b
fix debugEventsUntilDate logic
eli-darkly Oct 8, 2018
349e5b3
Merge pull request #113 from launchdarkly/eb/ch24449/less-metadata
eli-darkly Oct 8, 2018
d121bdd
integrate file data source into Go SDK
eli-darkly Oct 20, 2018
ac3e984
rename FileSourcePaths to FilePaths
eli-darkly Oct 20, 2018
bb55fbf
rename FileSourceLogger to UseLogger
eli-darkly Oct 20, 2018
6eeaab0
refactor file-watching logic, don't recreate FileDataSource
eli-darkly Oct 20, 2018
b023ab2
improve start logic
eli-darkly Oct 20, 2018
038b8ac
avoid extra reload on retry
eli-darkly Oct 20, 2018
7abd797
test fixes
eli-darkly Oct 20, 2018
3607b82
restore original logic
eli-darkly Oct 20, 2018
55257a4
reload function doesn't need to return an error
eli-darkly Oct 21, 2018
5ef116b
revert accidental commit
eli-darkly Oct 22, 2018
e85828e
Merge pull request #116 from launchdarkly/eb/refactor-file-watch
eli-darkly Oct 22, 2018
4feab2e
use a factory function for reloader
eli-darkly Oct 22, 2018
2a7e82c
Change where reload is called so we can't miss file changes
eli-darkly Oct 22, 2018
6ee84d8
break up a big function into several methods and a struct
eli-darkly Oct 25, 2018
6045efb
break things up some more
eli-darkly Oct 25, 2018
71e209a
move some stuff back out of the struct
eli-darkly Oct 26, 2018
73f1944
Merge pull request #115 from launchdarkly/eb/ch11136/file-data-source
eli-darkly Oct 26, 2018
47d738d
move some stuff back out of the struct
eli-darkly Oct 26, 2018
046facc
Merge branch 'eb/ch11136/file-data-source' into v4
eli-darkly Oct 26, 2018
01181c9
Merge branch 'v4' into eb/ch11136/processor-factory
eli-darkly Oct 26, 2018
3729e11
specify UpdateProcessor with a factory to simplify usage
eli-darkly Oct 26, 2018
955dc4b
linter
eli-darkly Oct 26, 2018
8a21d56
Merge pull request #117 from launchdarkly/eb/ch11136/processor-factory
eli-darkly Oct 26, 2018
10b0ba1
Merge branch 'v4' into jko/consul
eli-darkly Oct 26, 2018
eb38ee2
add Consul dependencies
eli-darkly Oct 26, 2018
a43d8a5
add basic tests for Consul store
eli-darkly Oct 26, 2018
3d93305
shorten type name to make linter happy
eli-darkly Oct 26, 2018
74b0602
make the Consul tests actually run
eli-darkly Oct 26, 2018
44c4d92
fix key mistake in All
eli-darkly Oct 26, 2018
42460b7
run Consul in all CI containers
eli-darkly Oct 26, 2018
95621f2
don't return an instance if we failed
eli-darkly Oct 26, 2018
50c83a0
refactor Redis concurrency tests so we can reuse the logic for Consul
eli-darkly Oct 26, 2018
5e6db56
add concurrent modification tests for Consul
eli-darkly Oct 26, 2018
1109312
refactoring
eli-darkly Oct 26, 2018
13791a5
simplify flow a little
eli-darkly Oct 26, 2018
c1ee6da
minor cleanup
eli-darkly Oct 26, 2018
d2972da
use varargs options
eli-darkly Oct 26, 2018
d53fa0b
test comment
eli-darkly Oct 26, 2018
61ec8ff
avoid confusion between our package name and the Consul package alias
eli-darkly Oct 26, 2018
de8936b
misc cleanup + comments
eli-darkly Oct 26, 2018
fc75337
add store test
eli-darkly Oct 26, 2018
57a9f37
update readme for Redis/Consul info
eli-darkly Oct 26, 2018
aff46da
Merge branch 'v4' into jko/consul
eli-darkly Oct 26, 2018
518c8bd
explanatory names
eli-darkly Oct 26, 2018
5960711
use channel for test data
eli-darkly Oct 26, 2018
9cd262d
don't export the implementation type
eli-darkly Oct 27, 2018
800da3e
Merge pull request #112 from launchdarkly/jko/consul
eli-darkly Oct 27, 2018
1f04d6e
Revert "Merge pull request #112 from launchdarkly/jko/consul"
eli-darkly Oct 30, 2018
1905dcf
merge from public after release
LaunchDarklyCI Oct 30, 2018
508c61d
check in initial Dynamo code with a few changes to support local/CI u…
eli-darkly Nov 6, 2018
12c50a1
linter
eli-darkly Nov 6, 2018
d30f90d
vendoring
eli-darkly Nov 6, 2018
249627e
rename exported type
eli-darkly Nov 6, 2018
f8a4913
rename package so it's not the same as DynamoDB API package
eli-darkly Nov 6, 2018
47a514f
add test for better coverage of Init
eli-darkly Nov 6, 2018
b992fc6
refactoring
eli-darkly Nov 6, 2018
3e2ee05
fix test command
eli-darkly Nov 6, 2018
4e1b661
readme
eli-darkly Nov 6, 2018
e833c6b
use varargs configuration pattern
eli-darkly Nov 6, 2018
19d405e
comments
eli-darkly Nov 6, 2018
af2fdd5
use NewSessionWithOptions rather than NewSession
eli-darkly Nov 6, 2018
aa23ca1
typo
eli-darkly Nov 6, 2018
0355e9c
comments
eli-darkly Nov 6, 2018
1295d11
make concurrent modification tests reusable and run them for DynamoDB
eli-darkly Nov 6, 2018
efd4afc
factor out caching support using a helper class
eli-darkly Nov 7, 2018
0694491
linter
eli-darkly Nov 7, 2018
169907d
Merge branch 'eb/ch26435/dynamodb-2-concurrent-tests' into eb/ch26435…
eli-darkly Nov 7, 2018
93dc6ea
add helper to Dynamo store
eli-darkly Nov 7, 2018
f2f98d2
reimplement feature store helper using another package and interface
eli-darkly Nov 8, 2018
a8630a2
remove old code, add tests for FeatureStoreWrapper
eli-darkly Nov 8, 2018
78da3e8
linter
eli-darkly Nov 8, 2018
320b6d2
fix blog URL
eli-darkly Nov 8, 2018
b18be1a
put a lock around inited state
eli-darkly Nov 8, 2018
88a61e4
add namespace to log messages
eli-darkly Nov 8, 2018
041ca52
Merge pull request #119 from launchdarkly/eb/ch26435/dynamodb
eli-darkly Nov 8, 2018
c0fadaf
Merge branch 'dynamo' into eb/ch26435/dynamodb-2-concurrent-tests
eli-darkly Nov 8, 2018
079b9cd
Merge pull request #120 from launchdarkly/eb/ch26435/dynamodb-2-concu…
eli-darkly Nov 8, 2018
25ba72b
Merge branch 'dynamo' into eb/ch26435/dynamodb-3-cache2
eli-darkly Nov 8, 2018
e3579c0
fix initialization state logic in both DynamoDB and Redis
eli-darkly Nov 8, 2018
4ab8c9c
misc cleanup
eli-darkly Nov 8, 2018
14806c8
misc cleanup
eli-darkly Nov 8, 2018
0aeedd8
Remove ashanbrown from codeowners
ashanbrown Nov 8, 2018
863c645
fix assertion problem in feature store tests
eli-darkly Nov 8, 2018
62e2a13
fix test
eli-darkly Nov 8, 2018
d864d0d
fix caching logic for upsert
eli-darkly Nov 8, 2018
a4bd6f9
Merge branch 'eb/ch26435/dynamodb-3-cache2' into eb/ch26435/dynamodb-…
eli-darkly Nov 8, 2018
38a21e2
Merge branch 'eb/ch26435/dynamodb-4-init' into eb/ch26435/dynamodb-5-…
eli-darkly Nov 8, 2018
ef81e0f
re-add Consul feature store
eli-darkly Nov 8, 2018
c9e78c0
linter
eli-darkly Nov 8, 2018
1102516
CI tests
eli-darkly Nov 8, 2018
ae84efd
readme
eli-darkly Nov 8, 2018
8ad2816
Merge branch 'v4' into dynamo
eli-darkly Nov 8, 2018
d1a6e71
Merge branch 'dynamo' into eb/ch26435/dynamodb-3-cache2
eli-darkly Nov 8, 2018
da00d8c
Merge branch 'eb/ch26435/dynamodb-3-cache2' into eb/ch26435/dynamodb-…
eli-darkly Nov 8, 2018
af87f47
Merge branch 'eb/ch26435/dynamodb-4-init' into eb/ch26435/dynamodb-5-…
eli-darkly Nov 8, 2018
76fe8e3
Merge branch 'consul' into eb/ch25990/consul
eli-darkly Nov 8, 2018
02bdac7
fix store wrapper tests
eli-darkly Nov 8, 2018
884d66d
Merge branch 'eb/ch26435/dynamodb-3-cache2' into eb/ch26435/dynamodb-…
eli-darkly Nov 8, 2018
2c497ff
Merge branch 'eb/ch26435/dynamodb-4-init' into eb/ch26435/dynamodb-5-…
eli-darkly Nov 8, 2018
ee5ad2a
Merge branch 'consul' into eb/ch25990/consul
eli-darkly Nov 8, 2018
833e55d
add new Redis store constructor + documentation
eli-darkly Nov 9, 2018
a849b73
revert change to CI script and add comment
eli-darkly Nov 9, 2018
4891651
FeatureStoreWrapper takes care of filtering out deleted items
eli-darkly Nov 9, 2018
3ac1596
comment clarification
eli-darkly Nov 9, 2018
f3826fa
add ability to provide full AWS config
eli-darkly Nov 12, 2018
3026280
Merge pull request #121 from launchdarkly/eb/ch26435/dynamodb-3-cache2
eli-darkly Nov 13, 2018
47f5dbe
Merge pull request #122 from launchdarkly/eb/ch26435/dynamodb-4-init
eli-darkly Nov 13, 2018
529bdcb
Merge pull request #123 from launchdarkly/eb/ch26435/dynamodb-5-fix-t…
eli-darkly Nov 13, 2018
906a394
Merge pull request #124 from launchdarkly/eb/ch25990/consul
eli-darkly Nov 13, 2018
9b7d272
Merge branch 'dynamo' into consul
eli-darkly Nov 13, 2018
2f835c4
Merge branch 'consul' into eb/ch26435/redis-cleanup
eli-darkly Nov 13, 2018
889056c
Merge pull request #125 from launchdarkly/eb/ch26435/redis-cleanup
eli-darkly Nov 13, 2018
76fe3f4
Merge branch 'eb/ch26435/dynamodb-6-config' into consul
eli-darkly Nov 13, 2018
01af4a2
update doc comments and readme to link to docs page about feature stores
eli-darkly Nov 13, 2018
200d4ed
fix link
eli-darkly Nov 15, 2018
34128f4
fix links
eli-darkly Nov 15, 2018
201ee45
Merge pull request #127 from launchdarkly/eb/feature-store-docs
eli-darkly Nov 15, 2018
3cd6aae
merge from public after release
LaunchDarklyCI Nov 15, 2018
874c3c9
change how items are marshaled for DynamoDB
eli-darkly Nov 15, 2018
95a1732
simplify map lookup
eli-darkly Nov 15, 2018
b944cf9
use constants
eli-darkly Nov 15, 2018
b41610c
linter
eli-darkly Nov 15, 2018
71fbb51
clarify comment
eli-darkly Nov 15, 2018
330abc4
Merge pull request #128 from launchdarkly/eb/dynamodb-marshaling-fix
eli-darkly Nov 15, 2018
1ace259
merge from public after release
LaunchDarklyCI Nov 15, 2018
3c157d5
add namespace prefix option for DynamoDB; add tests for prefixing
eli-darkly Nov 16, 2018
ec8a0a3
fix comment
eli-darkly Nov 16, 2018
b4c579a
Merge pull request #129 from launchdarkly/eb/dynamodb-prefix
eli-darkly Nov 17, 2018
7321e4b
merge from public after release
LaunchDarklyCI Nov 17, 2018
dc2b9fc
fix broken prefix feature in DynamoDB store
eli-darkly Nov 22, 2018
36a5346
add another test
eli-darkly Nov 22, 2018
7ea65d8
fix Consul tests
eli-darkly Nov 22, 2018
0555031
Revert "fix Consul tests"
eli-darkly Nov 22, 2018
61c6223
fix Consul tests
eli-darkly Nov 22, 2018
ec0d0fb
Merge pull request #130 from launchdarkly/eb/fix-dynamodb-prefixes
eli-darkly Nov 27, 2018
580d1b0
merge from public after release
LaunchDarklyCI Nov 27, 2018
765cf70
add ability to set full events URI
eli-darkly Dec 18, 2018
a92cd83
Merge pull request #131 from launchdarkly/eb/ch28051/events-endpoint
eli-darkly Dec 18, 2018
69d834e
make naming consistent
eli-darkly Dec 18, 2018
d60f244
Merge branch 'v4' of github.com:launchdarkly/go-client into v4
eli-darkly Dec 18, 2018
a4295f7
use dependency ordering for non-atomic feature stores
eli-darkly Jan 6, 2019
c4579e4
misc cleanup
eli-darkly Jan 6, 2019
c110acb
comment fix
eli-darkly Jan 6, 2019
f71e5d9
comment
eli-darkly Jan 9, 2019
4cdb158
Merge pull request #132 from launchdarkly/eb/ch29197/non-atomic-init
eli-darkly Jan 9, 2019
fcb45c6
merge from public after release
LaunchDarklyCI Jan 9, 2019
2ae3d91
misc readme updates
eli-darkly Feb 1, 2019
9515db3
C/C++
eli-darkly Feb 1, 2019
3db305b
Hr/azure (#134)
hroederld Feb 5, 2019
3745bf1
Merge pull request #133 from launchdarkly/eb/ch26463/readme
eli-darkly Feb 7, 2019
d5fc2a9
increase event queue size to 10000
bwoskow-ld Feb 28, 2019
423487f
Merge pull request #136 from launchdarkly/bw/ch33000/event-queue-size
bwoskow-ld Feb 28, 2019
bafbd50
Merge commit '50ee77949e33be12aa70f67e6d30457a51ac7ef0' into v4
eli-darkly Mar 13, 2019
78f897a
don't send identify or custom event without a user key
eli-darkly Apr 12, 2019
05d3a2a
don't return an error when there's no user, just log it
eli-darkly Apr 16, 2019
b209c7c
Merge pull request #137 from launchdarkly/eb/ch32176/no-user-no-event
eli-darkly Apr 16, 2019
c554197
remove many vendored files using dep prune options
eli-darkly Apr 25, 2019
088cd3f
Merge pull request #139 from launchdarkly/eb/dep-prune
eli-darkly Apr 25, 2019
04ad27b
update readme footer
eli-darkly Apr 26, 2019
4ad46f6
merge from public after release
LaunchDarklyCI Apr 26, 2019
8ba8031
apply readme/contributing templates and other changes before go-clien…
bwoskow-ld Apr 29, 2019
682e45c
updating titles (#143)
bwoskow-ld Apr 29, 2019
49f730f
change from go-client to go-server-sdk (#141)
bwoskow-ld Apr 29, 2019
817b347
remove codeclimate from circle build (#144)
bwoskow-ld Apr 29, 2019
4e59338
Bw/ch36752/remove codeclimate from build2 (#145)
bwoskow-ld Apr 30, 2019
af1ec26
Merge branch 'v4' of github.com:launchdarkly/go-server-sdk-private in…
bwoskow-ld May 3, 2019
19f7dba
merge from public after release
LaunchDarklyCI May 6, 2019
c74d72c
Merge branch 'v4' of github.com:launchdarkly/go-server-sdk into v4
bwoskow-ld May 6, 2019
440f77e
Merge branch 'v4' of github.com:launchdarkly/go-server-sdk-private in…
bwoskow-ld May 6, 2019
f5d018f
Add ability to customize HTTP client
eli-darkly Apr 24, 2019
d45cd25
avoid data race + don't set logger directly
eli-darkly May 20, 2019
479dbe8
Use a pure function, not an interface
eli-darkly May 22, 2019
5d49af6
revise to use a factory function, and remove httpcontrol package
eli-darkly May 22, 2019
06ba33a
Merge pull request #146 from launchdarkly/eb/ch37474/http-adapter
eli-darkly May 28, 2019
5a84211
fix bucketing by numeric user attribute
eli-darkly May 30, 2019
28b4b61
Merge pull request #147 from launchdarkly/eb/ch39847/bucket-int
eli-darkly Jun 10, 2019
620f1a1
merge from public after release
LaunchDarklyCI Jun 11, 2019
e3615bb
merge from public after release
LaunchDarklyCI Jun 11, 2019
a6fe332
Merge branch 'v4' of github.com:launchdarkly/go-server-sdk into v4
eli-darkly Jun 11, 2019
4eb6b32
use separate read timeout for stream connection
eli-darkly Jun 12, 2019
e3ce67e
rm debugging
eli-darkly Jun 12, 2019
189e4b0
Merge pull request #149 from launchdarkly/eb/ch40846/stream-timeouts
eli-darkly Jun 12, 2019
0766610
merge from public after release
LaunchDarklyCI Jun 13, 2019
a602fe1
merge from public after release
LaunchDarklyCI Jun 13, 2019
fd1bcbe
Merge branch 'v4' of github.com:launchdarkly/go-server-sdk into v4
eli-darkly Jun 13, 2019
d580735
log a warning when streaming connection fails (#150)
bwoskow-ld Jun 24, 2019
8dac39c
merge from public after release
LaunchDarklyCI Jul 2, 2019
6a1fabb
rename "input" and "buffer" to "inbox" and "outbox" for clarity
eli-darkly Jul 15, 2019
be1cb46
drop events if inbox is full
eli-darkly Jul 15, 2019
97bdb47
comments clarifying sync flush behavior
eli-darkly Jul 16, 2019
82460b1
Merge pull request #151 from launchdarkly/eb/ch42975/event-inbox-over…
eli-darkly Jul 16, 2019
96fa784
add helper packages for TLS & NTLM configuration
eli-darkly Jul 16, 2019
3ef2903
fix package ref
eli-darkly Jul 16, 2019
ec92863
debugging
eli-darkly Jul 17, 2019
160a104
don't really need go get?
eli-darkly Jul 17, 2019
8698d47
go test ./...
eli-darkly Jul 17, 2019
fd9f762
fix repo checkout dir name to match package
eli-darkly Jul 17, 2019
a42eb47
fix path again
eli-darkly Jul 17, 2019
afe0b19
misc build script cleanup, fix package name, test all packages
eli-darkly Jul 17, 2019
aa58d12
syntax error
eli-darkly Jul 17, 2019
dc7c337
fix variable
eli-darkly Jul 17, 2019
3a4eaaa
add placeholder credentials for DynamoDB
eli-darkly Jul 17, 2019
367a07a
Merge branch 'eb/fix-windows-build' into eb/ch43336/ntlm-proxy
eli-darkly Jul 17, 2019
4d394b3
example code
eli-darkly Jul 17, 2019
5cab411
add type alias
eli-darkly Jul 17, 2019
0cd85eb
simplify helper function usage
eli-darkly Jul 17, 2019
f61b801
fix factory logic
eli-darkly Jul 17, 2019
372be55
Merge pull request #153 from launchdarkly/eb/fix-windows-build
eli-darkly Jul 17, 2019
0bc1a29
run tests for new subpackages
eli-darkly Jul 18, 2019
4d90366
Merge pull request #152 from launchdarkly/eb/ch43336/ntlm-proxy
eli-darkly Jul 22, 2019
98d4321
add option to log evaluation errors (#154)
eli-darkly Jul 23, 2019
285dc32
preserve http.DefaultTransport defaults so proxy env vars work (#155)
eli-darkly Jul 23, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
remove old code, add tests for FeatureStoreWrapper
  • Loading branch information
eli-darkly committed Nov 8, 2018
commit a8630a2863939352c110550e85a3feb0d4d20ef7
143 changes: 0 additions & 143 deletions feature_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ import (
"log"
"os"
"sync"
"time"

cache "github.com/patrickmn/go-cache"
)

// FeatureStore is an interface describing a structure that maintains the live collection of features
Expand Down Expand Up @@ -161,143 +158,3 @@ func (store *InMemoryFeatureStore) Initialized() bool {
defer store.RUnlock()
return store.isInitialized
}

// FeatureStoreHelper is a helper type that can provide caching behavior for a FeatureStore
// implementation.
type FeatureStoreHelper struct {
cache *cache.Cache
cacheTTL time.Duration
}

// NewFeatureStoreHelper creates an instance of FeatureStoreHelper. If cacheTTL is
// non-zero, it will create an in-memory cache with the specified TTL and all of the
// FeatureStoreHelper methods will use this cache. If cacheTTL is zero, it will not
// create a cache and the methods will simply delegate to the underlying data store.
func NewFeatureStoreHelper(cacheTTL time.Duration) *FeatureStoreHelper {
ret := FeatureStoreHelper{cacheTTL: cacheTTL}
if cacheTTL > 0 {
ret.cache = cache.New(cacheTTL, 5*time.Minute)
}
return &ret
}

func featureStoreCacheKey(kind VersionedDataKind, key string) string {
return kind.GetNamespace() + ":" + key
}

func featureStoreAllItemsCacheKey(kind VersionedDataKind) string {
return "all:" + kind.GetNamespace()
}

// Init performs an update of the entire data store, with optional caching. The uncachedInit
// function updates the underlying data.
func (fsh *FeatureStoreHelper) Init(allData map[VersionedDataKind]map[string]VersionedData,
uncachedInit func(map[VersionedDataKind]map[string]VersionedData) error) error {
if fsh.cache == nil {
return uncachedInit(allData)
}
fsh.cache.Flush()
err := uncachedInit(allData)
if err != nil {
return err
}
for kind, items := range allData {
fsh.putAllItemsInCache(kind, items)
}
return nil
}

func (fsh *FeatureStoreHelper) putAllItemsInCache(kind VersionedDataKind, items map[string]VersionedData) {
if fsh.cache == nil {
return
}
// We do some filtering here so that deleted items are not included in the full cached data set
// that's used by All. This is so that All doesn't have to do that filtering itself. However,
// since Get does know to filter out deleted items, we will still cache those individually,
filteredItems := make(map[string]VersionedData, len(items))
for key, item := range items {
fsh.cache.Set(featureStoreCacheKey(kind, key), item, fsh.cacheTTL)
if !item.IsDeleted() {
filteredItems[key] = item
}
}
fsh.cache.Set(featureStoreAllItemsCacheKey(kind), filteredItems, fsh.cacheTTL)
}

// Get retrieves a single item by key, with optional caching. The uncachedGet function attempts
// to retrieve the item from the underlying data store.
func (fsh *FeatureStoreHelper) Get(kind VersionedDataKind, key string,
uncachedGet func(VersionedDataKind, string) (VersionedData, error)) (VersionedData, error) {
if fsh.cache == nil {
item, err := uncachedGet(kind, key)
return itemOnlyIfNotDeleted(item), err
}
cacheKey := featureStoreCacheKey(kind, key)
if data, present := fsh.cache.Get(cacheKey); present {
if data == nil { // If present is true but data is nil, we have cached the absence of an item
return nil, nil
}
if item, ok := data.(VersionedData); ok {
return itemOnlyIfNotDeleted(item), nil
}
}
// Item was not cached or cached value was not valid
item, err := uncachedGet(kind, key)
if err == nil {
fsh.cache.Set(cacheKey, item, fsh.cacheTTL)
}
return itemOnlyIfNotDeleted(item), err
}

func itemOnlyIfNotDeleted(item VersionedData) VersionedData {
if item != nil && item.IsDeleted() {
return nil
}
return item
}

// All retrieves all items of the specified kind, with optional caching. The uncachedAll function
// retrieves the items from the underlying data store.
func (fsh *FeatureStoreHelper) All(kind VersionedDataKind,
uncachedAll func(VersionedDataKind) (map[string]VersionedData, error)) (map[string]VersionedData, error) {
if fsh.cache == nil {
return uncachedAll(kind)
}
// Check whether we have a cache item for the entire data set
cacheKey := featureStoreAllItemsCacheKey(kind)
if data, present := fsh.cache.Get(cacheKey); present {
if items, ok := data.(map[string]VersionedData); ok {
return items, nil
}
}
// Data set was not cached or cached value was not valid
items, err := uncachedAll(kind)
if err == nil {
fsh.putAllItemsInCache(kind, items)
}
return items, err
}

// Upsert updates or adds an item, with optional caching. The uncachedUpsert function performs
// an upsert to the underlying data store.
func (fsh *FeatureStoreHelper) Upsert(kind VersionedDataKind, item VersionedData,
uncachedUpsert func(VersionedDataKind, VersionedData) error) error {
if fsh.cache == nil {
return uncachedUpsert(kind, item)
}
err := uncachedUpsert(kind, item)
if err == nil {
fsh.cache.Set(featureStoreCacheKey(kind, item.GetKey()), item, fsh.cacheTTL)
fsh.cache.Delete(featureStoreAllItemsCacheKey(kind))
}
return err
}

// Delete deletes an item, with optional caching. The uncachedUpsert function performs an
// upsert to the underlying data store - Delete is implemented by upserting an item that
// is in a deleted state.
func (fsh *FeatureStoreHelper) Delete(kind VersionedDataKind, key string, version int,
uncachedUpdate func(VersionedDataKind, VersionedData) error) error {
deletedItem := kind.MakeDeletedItem(key, version)
return fsh.Upsert(kind, deletedItem, uncachedUpdate)
}
222 changes: 0 additions & 222 deletions feature_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package ldclient_test

import (
"testing"
"time"

"github.com/stretchr/testify/require"
ld "gopkg.in/launchdarkly/go-client.v4"
ldtest "gopkg.in/launchdarkly/go-client.v4/shared_test"
)
Expand All @@ -16,223 +14,3 @@ func makeInMemoryStore() ld.FeatureStore {
func TestInMemoryFeatureStore(t *testing.T) {
ldtest.RunFeatureStoreTests(t, makeInMemoryStore)
}

func TestFeatureStoreHelper(t *testing.T) {
cacheTime := 30 * time.Second

getFunc := func(err error, item ld.VersionedData) func(ld.VersionedDataKind, string) (ld.VersionedData, error) {
return func(kind ld.VersionedDataKind, key string) (ld.VersionedData, error) {
return item, err
}
}

allFunc := func(err error, items ...ld.VersionedData) func(ld.VersionedDataKind) (map[string]ld.VersionedData, error) {
return func(kind ld.VersionedDataKind) (map[string]ld.VersionedData, error) {
ret := make(map[string]ld.VersionedData)
for _, item := range items {
ret[item.GetKey()] = item
}
return ret, err
}
}

initFunc := func(err error, wasCalled *bool) func(map[ld.VersionedDataKind]map[string]ld.VersionedData) error {
return func(allData map[ld.VersionedDataKind]map[string]ld.VersionedData) error {
if wasCalled != nil {
*wasCalled = true
}
return err
}
}

upsertFunc := func(err error, receivedItem *ld.VersionedData) func(ld.VersionedDataKind, ld.VersionedData) error {
return func(kind ld.VersionedDataKind, item ld.VersionedData) error {
if receivedItem != nil {
*receivedItem = item
}
return err
}
}

runCachedAndUncachedTests := func(t *testing.T, name string, test func(t *testing.T, isCached bool, fsh *ld.FeatureStoreHelper)) {
t.Run(name, func(t *testing.T) {
t.Run("uncached", func(t *testing.T) {
test(t, false, ld.NewFeatureStoreHelper(0))
})
t.Run("cached", func(t *testing.T) {
test(t, true, ld.NewFeatureStoreHelper(cacheTime))
})
})
}

runCachedAndUncachedTests(t, "Get", func(t *testing.T, isCached bool, fsh *ld.FeatureStoreHelper) {
flagv1 := ld.FeatureFlag{Key: "flag", Version: 1}
flagv2 := ld.FeatureFlag{Key: "flag", Version: 2}

item, err := fsh.Get(ld.Features, flagv1.Key, getFunc(nil, &flagv1))
require.NoError(t, err)
require.Equal(t, &flagv1, item)

item, err = fsh.Get(ld.Features, flagv1.Key, getFunc(nil, &flagv2))
require.NoError(t, err)
if isCached {
require.Equal(t, &flagv1, item) // returns cached value, does not call getter
} else {
require.Equal(t, &flagv2, item) // no caching, calls getter
}
})

runCachedAndUncachedTests(t, "Get with deleted item", func(t *testing.T, isCached bool, fsh *ld.FeatureStoreHelper) {
flagv1 := ld.FeatureFlag{Key: "flag", Version: 1, Deleted: true}
flagv2 := ld.FeatureFlag{Key: "flag", Version: 2, Deleted: false}

item, err := fsh.Get(ld.Features, flagv1.Key, getFunc(nil, &flagv1))
require.NoError(t, err)
require.Nil(t, item) // item is filtered out because Deleted is true

item, err = fsh.Get(ld.Features, flagv1.Key, getFunc(nil, &flagv2))
require.NoError(t, err)
if isCached {
require.Nil(t, item) // it used the cached deleted item rather than calling the getter
} else {
require.Equal(t, &flagv2, item) // no caching, calls getter
}
})

runCachedAndUncachedTests(t, "Get with missing item", func(t *testing.T, isCached bool, fsh *ld.FeatureStoreHelper) {
flag := ld.FeatureFlag{Key: "flag", Version: 1}

item, err := fsh.Get(ld.Features, flag.Key, getFunc(nil, nil))
require.NoError(t, err)
require.Nil(t, item)

item, err = fsh.Get(ld.Features, flag.Key, getFunc(nil, &flag))
require.NoError(t, err)
if isCached {
require.Nil(t, item) // the cache retains a nil result
} else {
require.Equal(t, &flag, item) // no caching, calls getter
}
})

t.Run("cached Get uses values from Init", func(t *testing.T) {
fsh := ld.NewFeatureStoreHelper(cacheTime)

flagv1 := ld.FeatureFlag{Key: "flag", Version: 1}
flagv2 := ld.FeatureFlag{Key: "flag", Version: 1}

allData := map[ld.VersionedDataKind]map[string]ld.VersionedData{
ld.Features: {flagv1.Key: &flagv1},
}
initWasCalled := false
err := fsh.Init(allData, initFunc(nil, &initWasCalled))
require.NoError(t, err)
require.True(t, initWasCalled)

item, err := fsh.Get(ld.Features, flagv1.Key, getFunc(nil, &flagv2))
require.NoError(t, err)
require.Equal(t, &flagv1, item) // it used the cached item rather than calling the getter
})

runCachedAndUncachedTests(t, "All", func(t *testing.T, isCached bool, fsh *ld.FeatureStoreHelper) {
flag1 := ld.FeatureFlag{Key: "flag1", Version: 1}
flag2 := ld.FeatureFlag{Key: "flag2", Version: 1}

items, err := fsh.All(ld.Features, allFunc(nil, &flag1, &flag2))
require.NoError(t, err)
require.Equal(t, 2, len(items))

items, err = fsh.All(ld.Features, allFunc(nil, &flag1))
require.NoError(t, err)
if isCached {
require.Equal(t, 2, len(items))
} else {
require.Equal(t, 1, len(items))
}
})

t.Run("cached All uses values from Init", func(t *testing.T) {
fsh := ld.NewFeatureStoreHelper(cacheTime)

flag1 := ld.FeatureFlag{Key: "flag1", Version: 1}
flag2 := ld.FeatureFlag{Key: "flag2", Version: 1}

allData := map[ld.VersionedDataKind]map[string]ld.VersionedData{
ld.Features: {flag1.Key: &flag1, flag2.Key: &flag2},
}
initWasCalled := false
err := fsh.Init(allData, initFunc(nil, &initWasCalled))
require.NoError(t, err)
require.True(t, initWasCalled)

items, err := fsh.All(ld.Features, allFunc(nil, &flag1))
require.NoError(t, err)
require.Equal(t, 2, len(items))
})

t.Run("cached All uses fresh values if there has been an update", func(t *testing.T) {
fsh := ld.NewFeatureStoreHelper(cacheTime)

flag1 := ld.FeatureFlag{Key: "flag1", Version: 1}
flag1v2 := ld.FeatureFlag{Key: "flag1", Version: 2}
flag2 := ld.FeatureFlag{Key: "flag2", Version: 1}
flag2v2 := ld.FeatureFlag{Key: "flag2", Version: 2}

allData := map[ld.VersionedDataKind]map[string]ld.VersionedData{
ld.Features: {flag1.Key: &flag1, flag2.Key: &flag2},
}
initWasCalled := false
err := fsh.Init(allData, initFunc(nil, &initWasCalled))
require.NoError(t, err)
require.True(t, initWasCalled)

err = fsh.Upsert(ld.Features, &flag1v2, upsertFunc(nil, nil))
require.NoError(t, err)

// updating any flag should force the cache for All to be flushed
items, err := fsh.All(ld.Features, allFunc(nil, &flag1v2, &flag2v2))
require.NoError(t, err)
require.Equal(t, 2, items[flag2.Key].GetVersion())
})

runCachedAndUncachedTests(t, "Upsert", func(t *testing.T, isCached bool, fsh *ld.FeatureStoreHelper) {
flagv1 := ld.FeatureFlag{Key: "flag1", Version: 1}
flagv2 := ld.FeatureFlag{Key: "flag1", Version: 2}

var receivedItem ld.VersionedData
err := fsh.Upsert(ld.Features, &flagv1, upsertFunc(nil, &receivedItem))
require.NoError(t, err)
require.Equal(t, &flagv1, receivedItem)

item, err := fsh.Get(ld.Features, flagv1.Key, getFunc(nil, &flagv2))
require.NoError(t, err)
if isCached {
require.Equal(t, &flagv1, item)
} else {
require.Equal(t, &flagv2, item)
}
})

runCachedAndUncachedTests(t, "Delete", func(t *testing.T, isCached bool, fsh *ld.FeatureStoreHelper) {
flagv1 := ld.FeatureFlag{Key: "flag1", Version: 1}
flagv2 := ld.FeatureFlag{Key: "flag1", Version: 2, Deleted: true}
flagv3 := ld.FeatureFlag{Key: "flag1", Version: 3}

item, err := fsh.Get(ld.Features, flagv1.Key, getFunc(nil, &flagv1))
require.NoError(t, err)
require.Equal(t, &flagv1, item)

var receivedItem ld.VersionedData
err = fsh.Delete(ld.Features, flagv1.Key, 2, upsertFunc(nil, &receivedItem))
require.NoError(t, err)
require.Equal(t, &flagv2, receivedItem)

item, err = fsh.Get(ld.Features, flagv1.Key, getFunc(nil, &flagv3))
require.NoError(t, err)
if isCached {
require.Nil(t, item)
} else {
require.Equal(t, &flagv3, item)
}
})
}
Loading