Git Product home page Git Product logo

Comments (8)

tillig avatar tillig commented on August 31, 2024

In the current system I'm not sure how to do that since it's two completely separate operations. You'd effectively have to keep every graph open forever "just in case" there's a child resolve that happens as part of one of these things.

It very much reminds me of distributed tracing in microservices and I wonder if there's something to adopt there. It also reminds me of a previous tracer that was getting developed for a while that was more like... an event handler would listen for events inside Autofac and write them to a named pipe. A listening UI/tracer would listen on that named pipe for messages and write them out. (That's something we could easily do now.)

You'd need the listener to the events to keep a database of all the operations going on, lifetime scope IDs, etc. As new resolve requests and operations occur, they get added to the database. When you view the trace, it's "whatever information we have at the time" - maybe the Func<T> resolve hasn't happened yet. In a minute or so, maybe it has. Viewing the graph at that point in time will be different.

I think that's the only way to solve it. It'd be a tremendous memory leak to try to do that inside the tracer in Autofac.

I think it may require a change in core Autofac, which might mean you want to move this to that repo instead of the graph repo, which is: All lifetime scope IDs (and component IDs, etc.) would need to be reasonably serializable. You'd want to be able to feed that information to the database and have it stored somehow, and compared later. I think right now lifetime scopes are all identified by their tags, which is just an object for an anonymous lifetime scope. You can't really serialize object equality.

from autofac.diagnostics.dotgraph.

alistairjevans avatar alistairjevans commented on August 31, 2024

Possibly, but I don't think it needs to be quite that complicated...I was just thinking of using AsyncLocal to store the current graph/diagnostics info instead of a concurrent dictionary, so anything fancy like Owned knows about the parent operation when it starts feeding diagnostic data back.

In addition any resolves of ILifetimeScope that we can't tie back to something concrete could add an explicit node that says "there's a dynamic dependency going on here, watch for that"?

from autofac.diagnostics.dotgraph.

tillig avatar tillig commented on August 31, 2024

I dunno, I feel like it might be more complicated than that, but maybe I'm overcomplicating it. Like, Func<Owned<T>> is going to be weird. It resolves a thing, but that thing has its own child lifetime scope. And in Func<T> they aren't necessarily guaranteed to actually execute it, so maybe there is something and maybe there's not.

I think AsyncLocal is going to get really messy when you combine injecting things through construction, Func<T> sorts of things, and manual service resolution based on HttpContext.RequestServices and such. A that point it's not really about graphing the resolve operation anymore - it's about graphing lifetime scope traces and I think... I think that's maybe outside the scope here. I mean, I get that it might be interesting, but I also think it's a different thing to be tracing.

That could potentially be assembled from distributed traces, since you'd have lifetime scope IDs, parent/child relationships, service/component relationships, all that. But, like, this:

public class Consumer
{
  private T _item;
  public Consumer(Func<T> factory)
  {
    _item = factory();
  }
}

is no different than this:

public class Consumer
{
  private T _item;
  public Consumer(ILifetimeScope scope)
  {
    _item = scope.Resolve<T>();
  }
}

or, in ASP.NET Core controllers, this:

public class Consumer
{
  private T _item;
  public Consumer()
  {
    _item = this.HttpContext.RequestServices.GetService<T>();
  }
}

In an ASP.NET Core context, yeah, there's sort of a logical correlation between lifetime scope and request. (Except when folks create unit of work scopes inside controller actions, which seems to be a common practice. I've seen a lot of StackOverflow questions come through about that.)

In other app types, there's not really as clear-cut a notion of one-lifetime-scope-per-logical-request sort of semantics. And if you add in multitenant scopes, that also gets interesting.

Point being... I think while some sort of AsyncLocal request stack might work in ASP.NET Core, I think what is actually stored and managed there is going to be complex; and I don't think that would work the way we expect in a console app or a Windows service or whatever.

However... you could totally address all of those situations if you were able to stream out IDs and activities and all that. The viewer/reporter could handle visualizing or filtering or whatever. You could even enable something such that the tracer could get data from the execution environment (maybe accessing HttpContext via IHttpContextAccessor to add metadata to a given graph and allow tying the resolution chain to a specific request. "Find me all the resolutions that happened during request ABCD1234" - and that would even tie to any other distributed trace data you might have.

That's really, really ASP.NET Core specific, of course.

In a Windows service TopShelf project, maybe you stream out stuff and want to see what happened in a certain time window rather than a certain resolve hierarchy because in your service everything resolves from the same lifetime scope. As a single graph, that'd suck. You'd want different ways to view the data based on different app types.

from autofac.diagnostics.dotgraph.

alistairjevans avatar alistairjevans commented on August 31, 2024

On reflection there's probably a significant amount of work here to make the diagnostics capture 'perfect', which we don't really need to do now, on this first cut of diagnostic support.

Perhaps for now we leave the behaviour mostly as it is, but with two (hopefully straightforward) proposed changes:

  • If the graph sees a resolve of ILifetimeScope or IComponentContext, we show a slightly different box, with words
    to the effect of "Possible Deferred Resolve Operation" or something, just to indicate to someone that they may need to
    go look at other traces.

  • The builder puts a counter somewhere on the generated diagram which is a global 'trace number'. This trace number is
    determined at the start of an operation, rather than the end, so if you see an indicator that you need to go look elsewhere,
    you may at least be able to correlate the two together by knowing which operation was logically 'next'.

    In a multi-threaded app, that trace number might not always literally be the next value, but at least it will imply some sense of
    order.

from autofac.diagnostics.dotgraph.

tillig avatar tillig commented on August 31, 2024

I like the ILifetimeScope different box type thing, I think that's doable.

On the trace number - I'll see your trace number and raise you "put DateTime.UtcNow down to the ms or us" so the ordering is based on time rather than sequence number. (Or, alternatively, use DateTime.UtcNow.Ticks as the number.) Sequence can be determined by the absolute time when the op starts.

from autofac.diagnostics.dotgraph.

tillig avatar tillig commented on August 31, 2024

I can add that in after the first PR/commit has been brought in, I think?

from autofac.diagnostics.dotgraph.

alistairjevans avatar alistairjevans commented on August 31, 2024

Yeah, I've merged that in now, so we can make these changes separately.

Time may be fine, yeah, but the granularity is going to be interesting, because there could be an extremely small time period between an operation and a nested operation.

from autofac.diagnostics.dotgraph.

alistairjevans avatar alistairjevans commented on August 31, 2024

Just adding this to the v6 release so we can track it.

from autofac.diagnostics.dotgraph.

Related Issues (5)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.