Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Make it easier to add new usage metrics. #1130

Merged
merged 11 commits into from
Nov 17, 2017
Merged

Make it easier to add new usage metrics. #1130

merged 11 commits into from
Nov 17, 2017

Conversation

grokys
Copy link
Contributor

@grokys grokys commented Aug 10, 2017

Currently, adding a new usage metric is a bit of a PITA. You need to:

  • Add a new field to UsageModel
  • Add a new method to IUsageTracker
  • Add a new method to UsageTrackerDispatcher
  • Add a new method to UsageTracker
  • Add the field to UsageTracker.ClearCounters
  • Add the field to UsageModel.Clone

This PR:

  • Removes the individual IncrementX methods from IUsageTracker and replaces them with an IncrementCounter method which takes a lambda describing the metric to update.
  • Uses reflection to reset the counters in UsageTracker.ClearCounters
  • Uses reflection to clone the counters in UsageModel.Clone
  • Moves IO and date comparison operations out of UsageTracker so it can be unit tested
  • Adds unit tests for UsageTracker

grokys added 3 commits August 10, 2017 14:49
- Separated IO and date/time comparisons into a separate `UsageService` to allow for easier unit testing
- Added some unit tests.
Instead of having a separate method for each usage metric, use a generialized method that takes an expression describing the metric to increment.
@grokys grokys force-pushed the refactor/usage-tracker branch from 9291c30 to 91d86ab Compare August 10, 2017 14:36
 Conflicts:
	src/GitHub.App/Models/RepositoryHost.cs
	src/GitHub.VisualStudio/source.extension.vsixmanifest
	src/MsiInstaller/Version.wxi
	src/common/SolutionInfo.cs
@grokys grokys changed the title WIP: Make it easier to add new usage metrics. Make it easier to add new usage metrics. Sep 6, 2017

async Task WriteAllTextAsync(string path, string text)
{
using (var s = File.OpenWrite(path))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be creating a new file, not overwriting the old one.

We need to truncate if file already exists.
Copy link
Contributor

@shana shana left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got a few questions and comments. Didn't go through all of it, so there might be more(tm) 😄
WRT splitting out IO so we can unit test, Rothko provides services just for that, fyi

@@ -11,6 +11,7 @@
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
<ApplicationVersion>2.3.1.0</ApplicationVersion>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change shouldn't be in here. Bad merge somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, bad merge - well spotted!


async Task<string> ReadAllTextAsync(string path)
{
using (var s = File.OpenRead(path))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use rothko for IO services that can be mocked for unit testing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already thought of that, but Rothko doesn't have the methods we need as far as I can tell, which is partly why I split this I/O functionality out into a separate service apart from the usage tracker itself.

async Task TimerTick()
{
await Initialize();

if (firstRun)
if (firstTick)
{
await IncrementLaunchCount();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IncrementLaunchCount calls LoadUsage which calls await Initialize which has already been called right before this. Do we need these double await Initialize calls?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, yes we can remove the Initialize at the top of TimerTick! 👍

Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
program.ApplicationName,
StoreFileName);
initialized = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a possibility of a race condition here where await Initialize gets called at the same time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a small possibility, if a counter is initialized on a background thread and while switching to the main thread, another counter is initialized. However all that would happen in that scenario is that services would get loaded twice, which I didn't think was a big enough problem to warrant putting a SemaphoreSlim or similar around.

The first tick will call `IncrementLaunchCount` which will call `LoadUsage` which will call `Initialize` for us.
@grokys
Copy link
Contributor Author

grokys commented Nov 8, 2017

@shana any chance of another review of this? I seem to remember you expressing surprise that this hadn't already gone in a few weeks back.

@shana shana merged commit 6c852c2 into master Nov 17, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants