diff --git a/.gitignore b/.gitignore index 7162ce7..07ef42d 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ bin *.idea /.vscode/* /untracked_*.go +cover.html +cover.out \ No newline at end of file diff --git a/Makefile b/Makefile index e2ac21f..8a69d67 100644 --- a/Makefile +++ b/Makefile @@ -16,12 +16,18 @@ build: ## Run tests test: go run ./main.go -exportfile ./test/test-space-export.json ./test/testapi - go test -count=1 ./... + go test ./... race: go run ./main.go -exportfile ./test/test-space-export.json ./test/testapi go test -race ./... +cover: + rm cover.out cover.html + go run ./main.go -exportfile ./test/test-space-export.json ./test/testapi + go test -cover -coverprofile cover.out -coverpkg=./test/testapi ./... + go tool cover -html=cover.out -o cover.html; open cover.html + .PHONY: lint ## Run linter lint: diff --git a/erm/templates/contentful_vo_lib.gotmpl b/erm/templates/contentful_vo_lib.gotmpl index 9cc9a1f..6641433 100644 --- a/erm/templates/contentful_vo_lib.gotmpl +++ b/erm/templates/contentful_vo_lib.gotmpl @@ -210,6 +210,8 @@ func (cc *ContentfulClient) CacheHasContentType(contentTypeID string) bool { if cc.Cache == nil { return false } + cc.cacheMutex.sharedDataGcLock.RLock() + defer cc.cacheMutex.sharedDataGcLock.RUnlock() for _, cachedContentType := range cc.Cache.contentTypes { if cachedContentType == contentTypeID { return true @@ -223,6 +225,8 @@ func (cc *ContentfulClient) ClientStats() { cc.logFn(nil, LogWarn, "ClientStats: no client available") return } + cc.cacheMutex.sharedDataGcLock.RLock() + defer cc.cacheMutex.sharedDataGcLock.RUnlock() if cc.logFn != nil { fieldsMap := map[string]interface{}{ "space ID": cc.SpaceID, @@ -235,7 +239,14 @@ func (cc *ContentfulClient) ClientStats() { if cc.cacheInit { fieldsMap["cache asset count"] = len(cc.Cache.assets) fieldsMap["cache entry count"] = len(cc.Cache.idContentTypeMap) - fieldsMap["cache parentMap length"] = len(cc.Cache.parentMap) + fieldsMap["cache parentMap children"] = len(cc.Cache.parentMap) + cc.cacheMutex.parentMapGcLock.RLock() + defer cc.cacheMutex.parentMapGcLock.RUnlock() + referenceCount := 0 + for _, parents := range cc.Cache.parentMap { + referenceCount += len(parents) + } + fieldsMap["cache parentMap parents"] = referenceCount } cc.logFn(fieldsMap, LogInfo, "Contentful ClientStats") } @@ -283,10 +294,13 @@ func (cc *ContentfulClient) GetAssetByID(id string, forceNoCache ...bool) (*cont if cc == nil || cc.Client == nil { return nil, errors.New("GetAssetByID: No client available") } - if cc.cacheInit && cc.Cache.assets != nil && (len(forceNoCache) == 0 || !forceNoCache[0]) { - cc.cacheMutex.assetsGcLock.Lock() + cc.cacheMutex.sharedDataGcLock.Lock() + cacheInit := cc.cacheInit + cc.cacheMutex.sharedDataGcLock.Unlock() + cc.cacheMutex.assetsGcLock.Lock() + defer cc.cacheMutex.assetsGcLock.Unlock() + if cacheInit && cc.Cache.assets != nil && (len(forceNoCache) == 0 || !forceNoCache[0]) { asset, okAsset := cc.Cache.assets[id] - cc.cacheMutex.assetsGcLock.Unlock() if okAsset { return asset, nil } else { @@ -583,6 +597,8 @@ func (cc *ContentfulClient) SetOfflineFallback(filename string) error { } func (cc *ContentfulClient) SetSyncMode(mode bool) error { + cc.cacheMutex.sharedDataGcLock.RLock() + defer cc.cacheMutex.sharedDataGcLock.RUnlock() if cc.offline { return errors.New("SetSyncMode: client is set offline, can't enable sync") } @@ -595,14 +611,18 @@ func (cc *ContentfulClient) ResetSync() { } func (cc *ContentfulClient) UpdateCache(ctx context.Context, contentTypes []string, cacheAssets bool) error { + cc.cacheMutex.sharedDataGcLock.RLock() ctxAtWork, cancel := context.WithTimeout(ctx, time.Second*time.Duration(cc.cacheUpdateTimeout)) - defer cancel() - if cc.offline { - ctxAtWork = ctx - } - if !cc.offline { - time.Sleep(time.Second * 2) - } + defer cancel() + localOffline := cc.offline + cc.cacheMutex.sharedDataGcLock.RUnlock() + + if localOffline { + ctxAtWork = ctx + } + if !localOffline { + time.Sleep(time.Second * 2) + } if contentTypes == nil { contentTypes = spaceContentTypes } else { @@ -647,7 +667,9 @@ func (cc *ContentfulClient) UpdateCache(ctx context.Context, contentTypes []stri func (cc *ContentfulClient) syncCache(ctx context.Context, contentTypes []string) error { start := time.Now() + cc.cacheMutex.sharedDataGcLock.Lock() cc.Cache.contentTypes = contentTypes + cc.cacheMutex.sharedDataGcLock.Unlock() if cc.logFn != nil && cc.logLevel <= LogInfo { cc.logFn(map[string]interface{}{"task": "syncCache"}, LogInfo, InfoCacheUpdateQueued) } @@ -658,8 +680,10 @@ func (cc *ContentfulClient) syncCache(ctx context.Context, contentTypes []string cc.syncToken, ) col.GetAll() + cc.cacheMutex.sharedDataGcLock.Lock() cc.syncToken = col.SyncToken cc.cacheInit = true + cc.cacheMutex.sharedDataGcLock.Unlock() if len(col.Items) == 0 { if cc.logFn != nil && cc.logLevel <= LogInfo { cc.logFn(map[string]interface{}{"time elapsed": fmt.Sprint(time.Since(start)), "task": "syncCache"}, LogInfo, InfoUpdateCacheTime) @@ -805,11 +829,21 @@ func (cc *ContentfulClient) cacheSpace(ctx context.Context, contentTypes []strin cc.logFn(map[string]interface{}{"time elapsed": fmt.Sprint(time.Since(start)), "task": "UpdateCache"}, LogInfo, InfoUpdateCacheTime) } cc.cacheMutex.fullCacheGcLock.Lock() - defer cc.cacheMutex.fullCacheGcLock.Unlock() - cc.Cache = tempCache cc.cacheMutex.sharedDataGcLock.Lock() + cc.cacheMutex.assetsGcLock.Lock() + cc.cacheMutex.idContentTypeMapGcLock.Lock() + cc.cacheMutex.parentMapGcLock.Lock() + {{ range $index , $contentType := $contentTypes }}cc.cacheMutex.{{ $contentType.Sys.ID }}GcLock.Lock() + {{ end }} + cc.Cache = tempCache cc.offline = offlinePreviousState + {{ range $index , $contentType := $contentTypes }}cc.cacheMutex.{{ $contentType.Sys.ID }}GcLock.Unlock() + {{ end }} + cc.cacheMutex.parentMapGcLock.Unlock() + cc.cacheMutex.idContentTypeMapGcLock.Unlock() + cc.cacheMutex.assetsGcLock.Unlock() cc.cacheMutex.sharedDataGcLock.Unlock() + cc.cacheMutex.fullCacheGcLock.Unlock() } func ToAssetReference(asset *contentful.Asset) (refSys ContentTypeSys) { @@ -922,12 +956,17 @@ func (cc *ContentfulClient) getAllAssets(tryCacheFirst bool) (map[string]*conten if cc == nil || cc.Client == nil { return nil, errors.New("getAllAssets: No client available") } - if cc.cacheInit && cc.Cache.assets != nil && tryCacheFirst { + cc.cacheMutex.sharedDataGcLock.RLock() + offline := cc.offline + cacheInit := cc.cacheInit + cc.cacheMutex.sharedDataGcLock.RUnlock() + + if cacheInit && cc.Cache.assets != nil && tryCacheFirst { return cc.Cache.assets, nil } allItems := []interface{}{} assets := map[string]*contentful.Asset{} - if cc.offline { + if offline { for _, asset := range cc.offlineTemp.Assets { allItems = append(allItems,asset) } diff --git a/erm/templates/contentful_vo_lib_contenttype.gotmpl b/erm/templates/contentful_vo_lib_contenttype.gotmpl index fcdf0d2..c2e28c3 100644 --- a/erm/templates/contentful_vo_lib_contenttype.gotmpl +++ b/erm/templates/contentful_vo_lib_contenttype.gotmpl @@ -20,10 +20,14 @@ func (cc *ContentfulClient) GetAll{{ firstCap $contentType.Sys.ID }}() (voMap ma if cc == nil { return nil, errors.New("GetAll{{ firstCap $contentType.Sys.ID }}: No client available") } - if cc.cacheInit { + cc.cacheMutex.sharedDataGcLock.RLock() + cacheInit := cc.cacheInit + optimisticPageSize := cc.optimisticPageSize + cc.cacheMutex.sharedDataGcLock.RUnlock() + if cacheInit { return cc.Cache.entryMaps.{{ $contentType.Sys.ID }}, nil } - col, err := cc.optimisticPageSizeGetAll("{{ $contentType.Sys.ID }}", cc.optimisticPageSize) + col, err := cc.optimisticPageSizeGetAll("{{ $contentType.Sys.ID }}", optimisticPageSize) if err != nil { return nil, err } @@ -740,9 +744,9 @@ func (cc *ContentfulClient) cache{{ firstCap $contentType.Sys.ID }}ByID(ctx cont // delete as child delete(cc.Cache.parentMap, id) // delete as parent - for childID, child := range cc.Cache.parentMap { + for childID, parents := range cc.Cache.parentMap { newParents := []EntryReference{} - for _, parent := range child { + for _, parent := range parents { if parent.ID != id { newParents = append(newParents, parent) } @@ -761,12 +765,14 @@ func (cc *ContentfulClient) cache{{ firstCap $contentType.Sys.ID }}ByID(ctx cont } cc.Cache.entryMaps.{{ $contentType.Sys.ID }}[id] = {{ $contentType.Sys.ID }} cc.Cache.idContentTypeMap[id] = {{ $contentType.Sys.ID }}.Sys.ContentType.Sys.ID + allChildrensIds := map[string]bool{} {{ range $fieldIndex, $field := $contentType.Fields }} {{ if fieldIsMultipleReference $field }} for _, loc := range cc.locales { children, okChildren := {{ $contentType.Sys.ID }}.Fields.{{ firstCap $field.ID }}[string(loc)] if okChildren { for _, child := range children { + allChildrensIds[child.Sys.ID] = true if cc.Cache.parentMap[child.Sys.ID] == nil { cc.Cache.parentMap[child.Sys.ID] = []EntryReference{} } @@ -787,6 +793,7 @@ func (cc *ContentfulClient) cache{{ firstCap $contentType.Sys.ID }}ByID(ctx cont for _, loc := range cc.locales { child, okChild := {{ $contentType.Sys.ID }}.Fields.{{ firstCap $field.ID }}[string(loc)] if okChild { + allChildrensIds[child.Sys.ID] = true if cc.Cache.parentMap[child.Sys.ID] == nil { cc.Cache.parentMap[child.Sys.ID] = []EntryReference{} } @@ -803,6 +810,20 @@ func (cc *ContentfulClient) cache{{ firstCap $contentType.Sys.ID }}ByID(ctx cont } {{ end }} {{ end }} + _ = allChildrensIds // safety net + // clean up child-parents that don't exist anymore + for childID, parents := range cc.Cache.parentMap { + if _, isCollectedChildID := allChildrensIds[childID]; isCollectedChildID { + continue + } + newParents := []EntryReference{} + for _, parent := range parents { + if parent.ID != id { + newParents = append(newParents, parent) + } + } + cc.Cache.parentMap[childID] = newParents + } return nil } diff --git a/main.go b/main.go index d7c72c7..6a26c33 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,7 @@ import ( "github.com/foomo/gocontentful/erm" ) -var VERSION = "v1.0.17" +var VERSION = "v1.0.18" type contentfulRc struct { ManagementToken string `json:"managementToken"` diff --git a/test/cache_test.go b/test/cache_test.go index 512796e..fc3fb77 100644 --- a/test/cache_test.go +++ b/test/cache_test.go @@ -10,6 +10,7 @@ import ( func TestCache(t *testing.T) { contentfulClient, err := getTestClient() + contentfulClient.ClientStats() require.NoError(t, err) stats, err := contentfulClient.GetCacheStats() require.NoError(t, err) @@ -17,6 +18,8 @@ func TestCache(t *testing.T) { require.Equal(t, 12, stats.AssetCount) require.Equal(t, 9, stats.EntryCount) require.Equal(t, 6, stats.ParentCount) + err = contentfulClient.SetSyncMode(true) + require.Error(t, err) } func TestBrokenReferences(t *testing.T) { @@ -32,6 +35,15 @@ func TestCacheHasContentType(t *testing.T) { require.True(t, contentfulClient.CacheHasContentType("brand")) } +func TestGetAsset(t *testing.T) { + contentfulClient, err := getTestClient() + require.NoError(t, err) + _, err = contentfulClient.GetAssetByID("Xc0ny7GWsMEMCeASWO2um") + require.NoError(t, err) + newAsset := testapi.NewAssetFromURL("12345", "https://example.com", "PNG", "New Asset") + require.NotNil(t, newAsset) +} + func TestDeleteAssetFromCache(t *testing.T) { contentfulClient, err := getTestClient() require.NoError(t, err) @@ -91,7 +103,7 @@ func TestPreserveCacheIfNewer(t *testing.T) { require.Equal(t, 2.0, brand.Sys.Version) } -func TestAddEntryAndSet(t *testing.T) { +func TestEntry(t *testing.T) { contentfulClient, err := getTestClient() require.NoError(t, err) cfProduct := testapi.NewCfProduct(contentfulClient) diff --git a/test/concurrency_test.go b/test/concurrency_test.go index 9bd45bd..23cdfc4 100644 --- a/test/concurrency_test.go +++ b/test/concurrency_test.go @@ -19,8 +19,26 @@ func readWorker(contentfulClient *testapi.ContentfulClient, i int) error { if err != nil { return err } + _, err = contentfulClient.GetAllProduct() + if err != nil { + return err + } price := product.Price() testLogger.Infof("Read worker %d read price: %f", i, price) + _ = product.Brand() + _ = product.Categories() + _ = product.Image() + _ = product.Nodes() + _ = product.ProductDescription() + _ = product.ProductName() + _ = product.Quantity() + _ = product.SeoText() + _ = product.Sizetypecolor() + _ = product.Sku() + _ = product.Slug() + _ = product.Tags() + _ = product.Website() + _ = product.GetPublishingStatus() return nil } @@ -48,6 +66,19 @@ func writeWorker(contentfulClient *testapi.ContentfulClient, i int) error { } contentfulClient.SetProductInCache(product) testLogger.Infof("Write worker %d set price: %d", i, i) + product.SetBrand(testapi.ContentTypeSys{}) + product.SetCategories([]testapi.ContentTypeSys{}) + product.SetImage([]testapi.ContentTypeSys{}) + product.SetNodes(nil) + product.SetProductDescription("") + product.SetProductName("") + product.SetQuantity(1) + product.SetSeoText("") + product.SetSizetypecolor("") + product.SetSku("") + product.SetSlug("") + product.SetTags([]string{""}) + product.SetWebsite("") return nil } diff --git a/test/testapi/gocontentfulvo.go b/test/testapi/gocontentfulvo.go index 765327b..05ebea2 100644 --- a/test/testapi/gocontentfulvo.go +++ b/test/testapi/gocontentfulvo.go @@ -1,4 +1,4 @@ -// Code generated by https://github.com/foomo/gocontentful v1.0.16 - DO NOT EDIT. +// Code generated by https://github.com/foomo/gocontentful v1.0.18 - DO NOT EDIT. package testapi import ( diff --git a/test/testapi/gocontentfulvobase.go b/test/testapi/gocontentfulvobase.go index 8cdc404..d13e37c 100644 --- a/test/testapi/gocontentfulvobase.go +++ b/test/testapi/gocontentfulvobase.go @@ -1,4 +1,4 @@ -// Code generated by https://github.com/foomo/gocontentful v1.0.16 - DO NOT EDIT. +// Code generated by https://github.com/foomo/gocontentful v1.0.18 - DO NOT EDIT. package testapi import "github.com/foomo/contentful" diff --git a/test/testapi/gocontentfulvolib.go b/test/testapi/gocontentfulvolib.go index a7b04de..b0dfe91 100644 --- a/test/testapi/gocontentfulvolib.go +++ b/test/testapi/gocontentfulvolib.go @@ -1,4 +1,4 @@ -// Code generated by https://github.com/foomo/gocontentful v1.0.16 - DO NOT EDIT. +// Code generated by https://github.com/foomo/gocontentful v1.0.18 - DO NOT EDIT. package testapi import ( @@ -227,6 +227,8 @@ func (cc *ContentfulClient) CacheHasContentType(contentTypeID string) bool { if cc.Cache == nil { return false } + cc.cacheMutex.sharedDataGcLock.RLock() + defer cc.cacheMutex.sharedDataGcLock.RUnlock() for _, cachedContentType := range cc.Cache.contentTypes { if cachedContentType == contentTypeID { return true @@ -240,6 +242,8 @@ func (cc *ContentfulClient) ClientStats() { cc.logFn(nil, LogWarn, "ClientStats: no client available") return } + cc.cacheMutex.sharedDataGcLock.RLock() + defer cc.cacheMutex.sharedDataGcLock.RUnlock() if cc.logFn != nil { fieldsMap := map[string]interface{}{ "space ID": cc.SpaceID, @@ -252,7 +256,14 @@ func (cc *ContentfulClient) ClientStats() { if cc.cacheInit { fieldsMap["cache asset count"] = len(cc.Cache.assets) fieldsMap["cache entry count"] = len(cc.Cache.idContentTypeMap) - fieldsMap["cache parentMap length"] = len(cc.Cache.parentMap) + fieldsMap["cache parentMap children"] = len(cc.Cache.parentMap) + cc.cacheMutex.parentMapGcLock.RLock() + defer cc.cacheMutex.parentMapGcLock.RUnlock() + referenceCount := 0 + for _, parents := range cc.Cache.parentMap { + referenceCount += len(parents) + } + fieldsMap["cache parentMap parents"] = referenceCount } cc.logFn(fieldsMap, LogInfo, "Contentful ClientStats") } @@ -300,10 +311,13 @@ func (cc *ContentfulClient) GetAssetByID(id string, forceNoCache ...bool) (*cont if cc == nil || cc.Client == nil { return nil, errors.New("GetAssetByID: No client available") } - if cc.cacheInit && cc.Cache.assets != nil && (len(forceNoCache) == 0 || !forceNoCache[0]) { - cc.cacheMutex.assetsGcLock.Lock() + cc.cacheMutex.sharedDataGcLock.Lock() + cacheInit := cc.cacheInit + cc.cacheMutex.sharedDataGcLock.Unlock() + cc.cacheMutex.assetsGcLock.Lock() + defer cc.cacheMutex.assetsGcLock.Unlock() + if cacheInit && cc.Cache.assets != nil && (len(forceNoCache) == 0 || !forceNoCache[0]) { asset, okAsset := cc.Cache.assets[id] - cc.cacheMutex.assetsGcLock.Unlock() if okAsset { return asset, nil } else { @@ -617,6 +631,8 @@ func (cc *ContentfulClient) SetOfflineFallback(filename string) error { } func (cc *ContentfulClient) SetSyncMode(mode bool) error { + cc.cacheMutex.sharedDataGcLock.RLock() + defer cc.cacheMutex.sharedDataGcLock.RUnlock() if cc.offline { return errors.New("SetSyncMode: client is set offline, can't enable sync") } @@ -629,12 +645,16 @@ func (cc *ContentfulClient) ResetSync() { } func (cc *ContentfulClient) UpdateCache(ctx context.Context, contentTypes []string, cacheAssets bool) error { + cc.cacheMutex.sharedDataGcLock.RLock() ctxAtWork, cancel := context.WithTimeout(ctx, time.Second*time.Duration(cc.cacheUpdateTimeout)) defer cancel() - if cc.offline { + localOffline := cc.offline + cc.cacheMutex.sharedDataGcLock.RUnlock() + + if localOffline { ctxAtWork = ctx } - if !cc.offline { + if !localOffline { time.Sleep(time.Second * 2) } if contentTypes == nil { @@ -681,7 +701,9 @@ func (cc *ContentfulClient) UpdateCache(ctx context.Context, contentTypes []stri func (cc *ContentfulClient) syncCache(ctx context.Context, contentTypes []string) error { start := time.Now() + cc.cacheMutex.sharedDataGcLock.Lock() cc.Cache.contentTypes = contentTypes + cc.cacheMutex.sharedDataGcLock.Unlock() if cc.logFn != nil && cc.logLevel <= LogInfo { cc.logFn(map[string]interface{}{"task": "syncCache"}, LogInfo, InfoCacheUpdateQueued) } @@ -692,8 +714,10 @@ func (cc *ContentfulClient) syncCache(ctx context.Context, contentTypes []string cc.syncToken, ) col.GetAll() + cc.cacheMutex.sharedDataGcLock.Lock() cc.syncToken = col.SyncToken cc.cacheInit = true + cc.cacheMutex.sharedDataGcLock.Unlock() if len(col.Items) == 0 { if cc.logFn != nil && cc.logLevel <= LogInfo { cc.logFn(map[string]interface{}{"time elapsed": fmt.Sprint(time.Since(start)), "task": "syncCache"}, LogInfo, InfoUpdateCacheTime) @@ -839,11 +863,25 @@ func (cc *ContentfulClient) cacheSpace(ctx context.Context, contentTypes []strin cc.logFn(map[string]interface{}{"time elapsed": fmt.Sprint(time.Since(start)), "task": "UpdateCache"}, LogInfo, InfoUpdateCacheTime) } cc.cacheMutex.fullCacheGcLock.Lock() - defer cc.cacheMutex.fullCacheGcLock.Unlock() - cc.Cache = tempCache cc.cacheMutex.sharedDataGcLock.Lock() + cc.cacheMutex.assetsGcLock.Lock() + cc.cacheMutex.idContentTypeMapGcLock.Lock() + cc.cacheMutex.parentMapGcLock.Lock() + cc.cacheMutex.brandGcLock.Lock() + cc.cacheMutex.categoryGcLock.Lock() + cc.cacheMutex.productGcLock.Lock() + + cc.Cache = tempCache cc.offline = offlinePreviousState + cc.cacheMutex.brandGcLock.Unlock() + cc.cacheMutex.categoryGcLock.Unlock() + cc.cacheMutex.productGcLock.Unlock() + + cc.cacheMutex.parentMapGcLock.Unlock() + cc.cacheMutex.idContentTypeMapGcLock.Unlock() + cc.cacheMutex.assetsGcLock.Unlock() cc.cacheMutex.sharedDataGcLock.Unlock() + cc.cacheMutex.fullCacheGcLock.Unlock() } func ToAssetReference(asset *contentful.Asset) (refSys ContentTypeSys) { @@ -966,12 +1004,17 @@ func (cc *ContentfulClient) getAllAssets(tryCacheFirst bool) (map[string]*conten if cc == nil || cc.Client == nil { return nil, errors.New("getAllAssets: No client available") } - if cc.cacheInit && cc.Cache.assets != nil && tryCacheFirst { + cc.cacheMutex.sharedDataGcLock.RLock() + offline := cc.offline + cacheInit := cc.cacheInit + cc.cacheMutex.sharedDataGcLock.RUnlock() + + if cacheInit && cc.Cache.assets != nil && tryCacheFirst { return cc.Cache.assets, nil } allItems := []interface{}{} assets := map[string]*contentful.Asset{} - if cc.offline { + if offline { for _, asset := range cc.offlineTemp.Assets { allItems = append(allItems, asset) } diff --git a/test/testapi/gocontentfulvolibbrand.go b/test/testapi/gocontentfulvolibbrand.go index f6025ef..ab22286 100644 --- a/test/testapi/gocontentfulvolibbrand.go +++ b/test/testapi/gocontentfulvolibbrand.go @@ -1,4 +1,4 @@ -// Code generated by https://github.com/foomo/gocontentful v1.0.16 - DO NOT EDIT. +// Code generated by https://github.com/foomo/gocontentful v1.0.18 - DO NOT EDIT. package testapi import ( @@ -22,10 +22,14 @@ func (cc *ContentfulClient) GetAllBrand() (voMap map[string]*CfBrand, err error) if cc == nil { return nil, errors.New("GetAllBrand: No client available") } - if cc.cacheInit { + cc.cacheMutex.sharedDataGcLock.RLock() + cacheInit := cc.cacheInit + optimisticPageSize := cc.optimisticPageSize + cc.cacheMutex.sharedDataGcLock.RUnlock() + if cacheInit { return cc.Cache.entryMaps.brand, nil } - col, err := cc.optimisticPageSizeGetAll("brand", cc.optimisticPageSize) + col, err := cc.optimisticPageSizeGetAll("brand", optimisticPageSize) if err != nil { return nil, err } @@ -800,9 +804,9 @@ func (cc *ContentfulClient) cacheBrandByID(ctx context.Context, id string, entry // delete as child delete(cc.Cache.parentMap, id) // delete as parent - for childID, child := range cc.Cache.parentMap { + for childID, parents := range cc.Cache.parentMap { newParents := []EntryReference{} - for _, parent := range child { + for _, parent := range parents { if parent.ID != id { newParents = append(newParents, parent) } @@ -821,7 +825,22 @@ func (cc *ContentfulClient) cacheBrandByID(ctx context.Context, id string, entry } cc.Cache.entryMaps.brand[id] = brand cc.Cache.idContentTypeMap[id] = brand.Sys.ContentType.Sys.ID - + allChildrensIds := map[string]bool{} + + _ = allChildrensIds // safety net + // clean up child-parents that don't exist anymore + for childID, parents := range cc.Cache.parentMap { + if _, isCollectedChildID := allChildrensIds[childID]; isCollectedChildID { + continue + } + newParents := []EntryReference{} + for _, parent := range parents { + if parent.ID != id { + newParents = append(newParents, parent) + } + } + cc.Cache.parentMap[childID] = newParents + } return nil } diff --git a/test/testapi/gocontentfulvolibcategory.go b/test/testapi/gocontentfulvolibcategory.go index a376770..9094533 100644 --- a/test/testapi/gocontentfulvolibcategory.go +++ b/test/testapi/gocontentfulvolibcategory.go @@ -1,4 +1,4 @@ -// Code generated by https://github.com/foomo/gocontentful v1.0.16 - DO NOT EDIT. +// Code generated by https://github.com/foomo/gocontentful v1.0.18 - DO NOT EDIT. package testapi import ( @@ -22,10 +22,14 @@ func (cc *ContentfulClient) GetAllCategory() (voMap map[string]*CfCategory, err if cc == nil { return nil, errors.New("GetAllCategory: No client available") } - if cc.cacheInit { + cc.cacheMutex.sharedDataGcLock.RLock() + cacheInit := cc.cacheInit + optimisticPageSize := cc.optimisticPageSize + cc.cacheMutex.sharedDataGcLock.RUnlock() + if cacheInit { return cc.Cache.entryMaps.category, nil } - col, err := cc.optimisticPageSizeGetAll("category", cc.optimisticPageSize) + col, err := cc.optimisticPageSizeGetAll("category", optimisticPageSize) if err != nil { return nil, err } @@ -564,9 +568,9 @@ func (cc *ContentfulClient) cacheCategoryByID(ctx context.Context, id string, en // delete as child delete(cc.Cache.parentMap, id) // delete as parent - for childID, child := range cc.Cache.parentMap { + for childID, parents := range cc.Cache.parentMap { newParents := []EntryReference{} - for _, parent := range child { + for _, parent := range parents { if parent.ID != id { newParents = append(newParents, parent) } @@ -585,7 +589,22 @@ func (cc *ContentfulClient) cacheCategoryByID(ctx context.Context, id string, en } cc.Cache.entryMaps.category[id] = category cc.Cache.idContentTypeMap[id] = category.Sys.ContentType.Sys.ID + allChildrensIds := map[string]bool{} + _ = allChildrensIds // safety net + // clean up child-parents that don't exist anymore + for childID, parents := range cc.Cache.parentMap { + if _, isCollectedChildID := allChildrensIds[childID]; isCollectedChildID { + continue + } + newParents := []EntryReference{} + for _, parent := range parents { + if parent.ID != id { + newParents = append(newParents, parent) + } + } + cc.Cache.parentMap[childID] = newParents + } return nil } diff --git a/test/testapi/gocontentfulvolibproduct.go b/test/testapi/gocontentfulvolibproduct.go index 705e7c6..95a0031 100644 --- a/test/testapi/gocontentfulvolibproduct.go +++ b/test/testapi/gocontentfulvolibproduct.go @@ -1,4 +1,4 @@ -// Code generated by https://github.com/foomo/gocontentful v1.0.16 - DO NOT EDIT. +// Code generated by https://github.com/foomo/gocontentful v1.0.18 - DO NOT EDIT. package testapi import ( @@ -22,10 +22,14 @@ func (cc *ContentfulClient) GetAllProduct() (voMap map[string]*CfProduct, err er if cc == nil { return nil, errors.New("GetAllProduct: No client available") } - if cc.cacheInit { + cc.cacheMutex.sharedDataGcLock.RLock() + cacheInit := cc.cacheInit + optimisticPageSize := cc.optimisticPageSize + cc.cacheMutex.sharedDataGcLock.RUnlock() + if cacheInit { return cc.Cache.entryMaps.product, nil } - col, err := cc.optimisticPageSizeGetAll("product", cc.optimisticPageSize) + col, err := cc.optimisticPageSizeGetAll("product", optimisticPageSize) if err != nil { return nil, err } @@ -1326,9 +1330,9 @@ func (cc *ContentfulClient) cacheProductByID(ctx context.Context, id string, ent // delete as child delete(cc.Cache.parentMap, id) // delete as parent - for childID, child := range cc.Cache.parentMap { + for childID, parents := range cc.Cache.parentMap { newParents := []EntryReference{} - for _, parent := range child { + for _, parent := range parents { if parent.ID != id { newParents = append(newParents, parent) } @@ -1347,11 +1351,13 @@ func (cc *ContentfulClient) cacheProductByID(ctx context.Context, id string, ent } cc.Cache.entryMaps.product[id] = product cc.Cache.idContentTypeMap[id] = product.Sys.ContentType.Sys.ID + allChildrensIds := map[string]bool{} for _, loc := range cc.locales { children, okChildren := product.Fields.Categories[string(loc)] if okChildren { for _, child := range children { + allChildrensIds[child.Sys.ID] = true if cc.Cache.parentMap[child.Sys.ID] == nil { cc.Cache.parentMap[child.Sys.ID] = []EntryReference{} } @@ -1371,6 +1377,7 @@ func (cc *ContentfulClient) cacheProductByID(ctx context.Context, id string, ent for _, loc := range cc.locales { child, okChild := product.Fields.Brand[string(loc)] if okChild { + allChildrensIds[child.Sys.ID] = true if cc.Cache.parentMap[child.Sys.ID] == nil { cc.Cache.parentMap[child.Sys.ID] = []EntryReference{} } @@ -1386,6 +1393,20 @@ func (cc *ContentfulClient) cacheProductByID(ctx context.Context, id string, ent } } + _ = allChildrensIds // safety net + // clean up child-parents that don't exist anymore + for childID, parents := range cc.Cache.parentMap { + if _, isCollectedChildID := allChildrensIds[childID]; isCollectedChildID { + continue + } + newParents := []EntryReference{} + for _, parent := range parents { + if parent.ID != id { + newParents = append(newParents, parent) + } + } + cc.Cache.parentMap[childID] = newParents + } return nil }