Skip to content

Commit

Permalink
Introduced a new version of the LocalAnalytics system, that uses zip …
Browse files Browse the repository at this point in the history
…based key value stores instead of real folders to increase performance
  • Loading branch information
cs-util committed Jan 21, 2024
1 parent 3d9e098 commit 4221445
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace com.csutil.logging.analytics {

[Obsolete("Use LocalAnalyticsV2 instead", true)]
[Obsolete("Use LocalAnalyticsV3 instead", true)]
public class LocalAnalytics : KeyValueStoreTypeAdapter<AppFlowEvent>, ILocalAnalytics {

private const string DEFAULT_DIR = "AppFlowAnalytics";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

namespace com.csutil.logging.analytics {

[Obsolete("Use LocalAnalyticsV3 instead")]
public class LocalAnalyticsV2 : KeyValueStoreTypeAdapter<AppFlowEvent>, ILocalAnalytics {

public IReadOnlyDictionary<string, KeyValueStoreTypeAdapter<AppFlowEvent>> categoryStores => _categoryStores;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using com.csutil.keyvaluestore;
using Zio;

namespace com.csutil.logging.analytics {

public class LocalAnalyticsV3 : KeyValueStoreTypeAdapter<AppFlowEvent>, ILocalAnalytics {

/// <summary> If the last 10 logs are missing if the app crashes this is probably ok, and not storing after every event increases performance </summary>
private const int maxAllowedOpenChanges = 10;

public IReadOnlyDictionary<string, KeyValueStoreTypeAdapter<AppFlowEvent>> categoryStores => _categoryStores;

private readonly Dictionary<string, KeyValueStoreTypeAdapter<AppFlowEvent>> _categoryStores
= new Dictionary<string, KeyValueStoreTypeAdapter<AppFlowEvent>>();

private readonly DirectoryEntry _dir;
private readonly object _threadLock = new object();

public LocalAnalyticsV3(DirectoryEntry dirForEvents)
: base(new ObservableKeyValueStore(ZipFileBasedKeyValueStore.New(dirForEvents.GetChild("AllEvents.zip"), maxAllowedOpenChanges))) {
this._dir = dirForEvents;
}

public override async Task<AppFlowEvent> Set(string key, AppFlowEvent value) {
var replacedEvent = await base.Set(key, value);
await GetStoreForCategory(value.cat).Set(key, value);
return replacedEvent;
}

public override async Task<bool> Remove(string key) {
var res = await base.Remove(key);
foreach (var s in _categoryStores.Values) { res &= await s.Remove(key); }
return res;
}

public override async Task RemoveAll() {
await base.RemoveAll();
foreach (var s in _categoryStores.Values) { await s.RemoveAll(); }
}

public KeyValueStoreTypeAdapter<AppFlowEvent> GetStoreForCategory(string catMethod) {
lock (_threadLock) {
if (_categoryStores.TryGetValue(catMethod, out var store)) { return store; }
var createdStore = ZipFileBasedKeyValueStore.New(_dir.GetChild(catMethod + ".zip"), maxAllowedOpenChanges).GetTypeAdapter<AppFlowEvent>();
_categoryStores.Add(catMethod, createdStore);
return createdStore;
}
}

public void Dispose() {
store.Dispose();
foreach (var s in categoryStores.Values) { s.store.Dispose(); }
_categoryStores.Clear();
}

}

}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public static async Task<ProgressionSystem<FeatureFlag>> Setup(KeyValueStoreType
}

public static async Task<ProgressionSystem<FeatureFlag>> SetupV2(KeyValueStoreTypeAdapter<FeatureFlag> featureFlagStore, DirectoryEntry localAnalyticsFolder, HashSet<Tuple<object, Type>> collectedInjectors = null) {
return await SetupV2(featureFlagStore, new LocalAnalyticsV2(localAnalyticsFolder), collectedInjectors);
return await SetupV2(featureFlagStore, new LocalAnalyticsV3(localAnalyticsFolder), collectedInjectors);
}

[Obsolete("Use SetupV2 instead")]
Expand All @@ -75,7 +75,7 @@ public static async Task<ProgressionSystem<FeatureFlag>> Setup(KeyValueStoreType
return xpSystem;
}

public static async Task<ProgressionSystem<FeatureFlag>> SetupV2(KeyValueStoreTypeAdapter<FeatureFlag> featureFlagStore, LocalAnalyticsV2 analytics, HashSet<Tuple<object, Type>> collectedInjectors = null) {
public static async Task<ProgressionSystem<FeatureFlag>> SetupV2(KeyValueStoreTypeAdapter<FeatureFlag> featureFlagStore, LocalAnalyticsV3 analytics, HashSet<Tuple<object, Type>> collectedInjectors = null) {
var ffm = new FeatureFlagManager<FeatureFlag>(featureFlagStore);
var injector1 = IoC.inject.SetSingleton(ffm);
collectedInjectors?.Add(new Tuple<object, Type>(injector1, typeof(FeatureFlagManager<FeatureFlag>)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public async Task TestLocalAnalytics() {

// Create a LocalAnalytics instance that uses only memory stores for testing:
var dir = EnvironmentV2.instance.GetNewInMemorySystem();
ILocalAnalytics localAnalytics = new LocalAnalyticsV2(dir);
ILocalAnalytics localAnalytics = new LocalAnalyticsV3(dir);

// Pass this local analytics system to the app flow impl. as the target store:
AppFlowToStore appFlow = new AppFlowToStore(localAnalytics);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ public async Task TestProgressiveDisclosure() {

// Set the store to be the target of the local analytics so that whenever any
var dir = EnvironmentV2.instance.GetNewInMemorySystem();
ILocalAnalytics analytics = new LocalAnalyticsV2(dir);
ILocalAnalytics analytics = new LocalAnalyticsV3(dir);
var store = analytics.store as ObservableKeyValueStore;
// Lets assume the users xp correlates with the number of triggered local analytics events:
store.CollectionChanged += delegate {
Expand Down Expand Up @@ -232,7 +232,7 @@ private async Task RunTestDefaultProgressionSystem(IKeyValueStore googleSheetsSt
cleanup.AddInjectorCleanup<FeatureFlagManager<FeatureFlag>>(injector1);

var dir = EnvironmentV2.instance.GetNewInMemorySystem();
ILocalAnalytics analytics = new LocalAnalyticsV2(dir);
ILocalAnalytics analytics = new LocalAnalyticsV3(dir);
AppFlow.AddAppFlowTracker(new AppFlowToStore(analytics));
var xpSystem = new ProgressionSystem<FeatureFlag>(analytics, ffm);
var injector2 = IoC.inject.SetSingleton<IProgressionSystem<FeatureFlag>>(xpSystem);
Expand Down Expand Up @@ -345,7 +345,7 @@ private static async Task<ProgressionSystem<FeatureFlag>> NewInMemoryTestXpSyste
var googleSheetsStore = new GoogleSheetsKeyValueStore(cachedFlags, apiKey, sheetId, sheetName);
var cachedFlagsLocalData = new InMemoryKeyValueStore();
var dir = EnvironmentV2.instance.GetNewInMemorySystem();
var analytics = new LocalAnalyticsV2(dir);
var analytics = new LocalAnalyticsV3(dir);
var featureFlagStore = new FeatureFlagStore(cachedFlagsLocalData, googleSheetsStore);
return await DefaultProgressionSystem.SetupV2(featureFlagStore, analytics, collectedInjectors);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ private static ILocalAnalytics CreateLocalAnalyticsSystem() {

private static ILocalAnalytics CreateLocalAnalyticsSystemV2() {
var dir = EnvironmentV2.instance.GetNewInMemorySystem();
LocalAnalyticsV2 analytics = new LocalAnalyticsV2(dir);
LocalAnalyticsV3 analytics = new LocalAnalyticsV3(dir);
// Setup the AppFlow logic to use the LocalAnalytics system:
AppFlow.AddAppFlowTracker(new AppFlowToStore(analytics));
return analytics;
Expand Down

0 comments on commit 4221445

Please sign in to comment.