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

Add MetricsFactory.remove(Metric) to allow metric lifetime management #2 #3

Closed
wants to merge 2 commits into from

Conversation

ktoso
Copy link
Member

@ktoso ktoso commented Apr 6, 2019

Implements #2
Based on top of #1 which should land without issues I think, so saving this PR some rebasing work

Rebased and adjusted from tomerd/swift-server-metrics-api-proposal#11

Feedback welcome, see for more details in the previous PR as well as ticket tomerd/swift-server-metrics-api-proposal#6 as well.

Note that we now don't have a caching one since it was only in the examples, and that's the type of implementations where the release matters the most. So the test is a bit weird, but since the test metrics store internally anyway the test does cover that the right things happen, even if it does not return the "cached one"

@swift-server-bot
Copy link

Can one of the admins verify this patch?

@ktoso ktoso force-pushed the wip-release-spi-new-repo-ktoso branch from 852ba80 to 1a22cb6 Compare April 6, 2019 04:19
@ktoso ktoso requested a review from tomerd April 6, 2019 04:21
@tomerd
Copy link
Member

tomerd commented Apr 9, 2019

@swift-server-bot test this please

@ktoso
Copy link
Member Author

ktoso commented Apr 9, 2019

I'll rebase again ;)

@ktoso ktoso force-pushed the wip-release-spi-new-repo-ktoso branch 2 times, most recently from 92c1594 to e524f81 Compare April 9, 2019 03:00
@tomerd
Copy link
Member

tomerd commented Apr 9, 2019

@ktoso so sorry about the moving target here :/

@ktoso
Copy link
Member Author

ktoso commented Apr 9, 2019

And another rebase 😉

Hehe, I don't mind, no worries :-)

@ktoso ktoso force-pushed the wip-release-spi-new-repo-ktoso branch from e524f81 to e0cae8b Compare April 9, 2019 03:03
@ktoso ktoso force-pushed the wip-release-spi-new-repo-ktoso branch from df8ed45 to c70830b Compare April 9, 2019 05:13
Sources/CoreMetrics/Metrics.swift Outdated Show resolved Hide resolved
@ktoso
Copy link
Member Author

ktoso commented Apr 11, 2019

Working with Tom on the Solution 2 version without having to depend on Foundation, promising progress there... Could replace this explicit API I think, update soon 👍

@ktoso
Copy link
Member Author

ktoso commented Apr 11, 2019

Update; I think the ARC + "weak registries" can effectively replace this explicit release style, PR here: #3

Only trouble with that is to make people aware that if they keep strong refs inside their metrics impls its probably a not good idea™

/// Reset the counter back to zero.
func reset()
}

/// All metric types conform to this protocol, allowing for APIs accepting "any metric."
public protocol Metric {}
Copy link
Member

Choose a reason for hiding this comment

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

this is an empty protocol, what's the benefit of it over Any? Just as a marker?

Copy link
Member Author

Choose a reason for hiding this comment

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

to be able to implement release(Metric), though if we moved release() onto the types themselves it would not be necessary.

Copy link
Member

Choose a reason for hiding this comment

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

yeah, moving that onto the types makes more sense I think.

Copy link
Member Author

Choose a reason for hiding this comment

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

Happy to move around, people had varying opinions on that, see also the #11 impl, which avoids needing the release() call and rides on ARC, though at somewhat more complex impl cost...

Copy link
Member Author

Choose a reason for hiding this comment

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

Note: moving onto the types gets us a bit cyclic references which we then have to break (factory has registry has metric; metric has factory to remove itself from the factory when remove() is called). It can be broken ofc, but let's see what others think... Also notable that all other libraries I've researched have the removal on the factory/registry types.

Copy link
Member Author

Choose a reason for hiding this comment

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

See also 3036b06

// alternatively, delete the Metric type and provide overloads:
// func remove<C: Counter>(_ counter: C)
// func remove<R: Recorder>(_ recorder: R)
// func remove<T: Timer>(_ timer: T)
Copy link
Member

@tomerd tomerd Apr 12, 2019

Choose a reason for hiding this comment

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

i slightly prefer to be symmetric with the makeXXX(label:) APIs and offer destroyXXX(label:) instead of the generic version. this could also allow us to get rid of the Metric protocol to keep things simple(r). wdyt?

Copy link
Member

Choose a reason for hiding this comment

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

@ktoso after sleeping on this some i now even more in favor of the above suggestion for the following reasons:

  1. api symmetry
  2. no need in marker protocol
  3. less coupling and confusion between the handler type that the backend libraries deal with (eg CounterHandler) and the metrics object which is what the apps and libraries deal with (eg Counter). iow, the backend libraries should only deal with handler types, and as such it means a release function that takes a metric object will need to poke a hole in the abstraction which is not very nice
  4. it opens the door to releasing by an expression which could be useful for middleware type libraries that have a bunch of metrics per connection or request and a naming convention around that

Copy link
Member

Choose a reason for hiding this comment

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

I'm also in favour of anything that doesn't use the weird marker protocol. makeXXX and destroyXXX sound good to me!

Copy link
Member

Choose a reason for hiding this comment

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

or obviously myMetric.destroy(), possibly I like this even more...

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, some good points, though I'm worried about the destroyCounter(label: "bla") losing type-safety and forces more of the "get it right" dance onto users / libraries where as a destroyCounter(counter) helps to get it right by compilation;

Example:

  • imagine if you refactor a metrics from counter to gauge let myThingy = Gauge(...) // was Counter -- there you had to since you want to get the diff type
  • you could forget to change the destroyCounter(myThingy.label) call, even if you changed the make site

With accepting destroyCounter(myThingy) you would get a compile error and would notice that you'd almost messed up, but the compiler helped you spot that.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, I see what you mean -- that if I don't release I (user) only touch Counter, but if I want to release I suddenly need to reach into all the way to Factory.

Let me see how it could look like. Main concern was the requirement of additional storage for the reference to the factory, but perhaps -- and also since we enforce that "bootstrap only once" deletes could reach through the shared field...? A bit iffy... could cause weird issues of "release in wrong logging library instance" happening 🤔

I'll give it a shot as separate commit and we can look at it here

Copy link

Choose a reason for hiding this comment

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

Is there a reason we don't benefit from the fact that all ...Handler protocols are reference types (AnyObject) to ask backend libraries to do their cleanup in deinit?

Copy link
Member Author

@ktoso ktoso Apr 16, 2019

Choose a reason for hiding this comment

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

Thanks for chiming in @hartbit :)
Sadly the deinit style has a lot of insane corner cases, and is somewhat abusing the mechanism (the lifecycles of "the metric" do not necessarily match 1:1 "the object"'s lifecycle), I explored it in the writeup over here: #2

I think we are getting to a final design here slowly... we'll be able to:

  • keep full typesafety of the releases -- no need to cast
  • no crossing boundaries between handler+factory (impl) and counter+system (user api)
  • destroy() can live on the objects themselves, leading to simple use and discovery of the feature
  • only place to care about "implementing destroying" remains factory, which semantically makes sense

I'll PR this in a moment with cleanups along the way;
I'm starting to see a light at the end of the tunnel here

Copy link

@hartbit hartbit Apr 16, 2019

Choose a reason for hiding this comment

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

Thanks for the explanation! It's true that ARC does come with design challenges, but I'm also worried about a destroy function:

  • Because destroy will sometimes be a no-op, sometimes not (depending on the implementation), users might misuse them but think everything is fine (because they are using a backend that is a no-op) but start having issues as soon as they switch backends.
  • A destroy function doesn't protect users from continuing to use the metrics objects after being destroyed.

While using ARC might mean abusing it slightly, and cause slightly more headaches in the implementation aspect of things, I does come with greater simplicity for the user.

Copy link
Member

Choose a reason for hiding this comment

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

/cc @weissi who was part of the previous ARC discussion

@ktoso ktoso changed the title Add MetricsSystem.release() to allow metric lifetime management #2 Add MetricsFactory.remove(Metric) to allow metric lifetime management #2 Apr 14, 2019
@ktoso ktoso closed this Apr 16, 2019
gjcairo pushed a commit to gjcairo/swift-metrics that referenced this pull request Feb 1, 2023
* Implement system metrics

* Update contributor list

* Point to 2.1.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants