-
Notifications
You must be signed in to change notification settings - Fork 18
Conversation
1300 lines 😩 |
@shishirx34 - Apologies. It should be an easy read for its length, however. It's a lot of interfaces and relatively simple code. |
@@ -105,7 +106,8 @@ Invoke-BuildStep 'Creating artifacts' { ` | |||
"src\NuGet.Services.ServiceBus\NuGet.Services.ServiceBus.csproj", ` | |||
"src\NuGet.Services.Validation\NuGet.Services.Validation.csproj", ` | |||
"src\NuGet.Services.Validation.Issues\NuGet.Services.Validation.Issues.csproj", ` | |||
"src\NuGet.Services.Sql\NuGet.Services.Sql.csproj" | |||
"src\NuGet.Services.Sql\NuGet.Services.Sql.csproj", ` | |||
"src\NuGet.Services.Status\NuGet.Services.Status.csproj" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
csproj [](start = 61, length = 6)
Shouldn't you update test.ps1
too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
IEnumerable<IComponent> subComponents) | ||
: this(name, description) | ||
{ | ||
SubComponents = subComponents?.Select(s => new SubComponent(s, this)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Select [](start = 43, length = 6)
You should ToList
this. The IEnumerable
could be some crazy LINQ query that people will have to enumerate every time they call SubComponents
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Also made this fix to several other component classes
public string Description { get; } | ||
public abstract ComponentStatus Status { get; set; } | ||
public IEnumerable<IComponent> SubComponents { get; } | ||
IEnumerable<IReadOnlyComponent> IReadOnlyComponent.SubComponents => SubComponents; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IEnumerable [](start = 8, length = 11)
Should this guy be public?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed the inheritance so that this property no longer needs to exist.
|
||
if (component == null) | ||
{ | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
break [](start = 20, length = 5)
If I run GetByPath('root', 'this', 'doesnt', 'exist')
and only the root component exists, this will return the root IReadOnlyComponent
. Would it be better if this returned null
instead? If so, I would rename the method to be GetByPathOrNull
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a bug. I added tests to make sure it doesn't break again.
|
||
namespace NuGet.Services.Status | ||
{ | ||
public class Message : IMessage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Message [](start = 17, length = 7)
Does this and Event
need interfaces? What value does it add?
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using Newtonsoft.Json; | ||
using System; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
System [](start = 6, length = 6)
Looks like that VS setting to put System
on top got turned off :(
<Version>9.0.1</Version> | ||
</PackageReference> | ||
</ItemGroup> | ||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import [](start = 3, length = 6)
This project should be signed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
forgot about that, added now
/// Gets the subcomponent of <paramref name="component"/> by iterating its subcomponents in the order of <paramref name="componentNames"/>. | ||
/// If none exists, returns <c>null</c>. | ||
/// </summary> | ||
public static TComponent GetByNames<TComponent>(this TComponent component, params string[] componentNames) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
component [](start = 72, length = 9)
Extension methods let the this
parameter be null, thus, you should check if component
is null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch
src/NuGet.Services.Status/Status.cs
Outdated
/// <summary> | ||
/// Describes the status of an entire service. | ||
/// </summary> | ||
public class Status |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use a more specific name -- ServiceStatus? NuGetServiceStatus?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like NuGetStatus
is a little redundant given that it's in the namespace NuGet.Services.Status
. Additionally, there's nothing NuGet
specific to this class (or any of the classes in this project).
I think ServiceStatus
makes sense.
src/NuGet.Services.Status/Status.cs
Outdated
/// <summary> | ||
/// The <see cref="IReadOnlyComponent"/> that describes the entire service. Its <see cref="IReadOnlyComponent.SubComponents"/> represent portions of the service. | ||
/// </summary> | ||
public IReadOnlyComponent RootComponent { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does RootComponent
represent? Gallery?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RootComponent
is the parent of Gallery, Search, our CDN, etc.
Basically, it's a component that encapsulates the entire service and reports a status for the entire service.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I will rename this to ServiceRootComponent
.
/// <summary> | ||
/// A list of subcomponents that make up this part of the service. | ||
/// </summary> | ||
new IEnumerable<IReadOnlyComponent> SubComponents { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is new
necessary? Why can't it reuse the IComponentRoot<T>.SubComponents
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll remove this.
/// <summary> | ||
/// A list of writable subcomponents that make up this part of the service. | ||
/// </summary> | ||
new IEnumerable<IComponent> SubComponents { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is new
necessary? Why can't it reuse
IReadOnlyComponent.SubComponentsor
IComponentRoot.SubComponents`?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll remove this, IComponentRoot<IComponent>.SubComponents
is sufficient?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forgot that this needs to exist so that IComponent.SubComponents
is not ambiguous between IEnumerable<IReadOnlyComponent>
and IEnumerable<IComponent>
.
/// <summary> | ||
/// Default implementation of <see cref="IComponent"/>. | ||
/// </summary> | ||
public abstract class Component : ReadOnlyComponent, IComponent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have examples somewhere of what the the hierarchy of these components is? What LeafComponents, PrimarySecondaryComponents, TreeComponents, etc.
Could there be more specific name? NuGetServiceComponent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify:
A consumer of status (gallery, status page) is going to only work with IReadOnlyComponent
and its implementation, ReadOnlyComponent
. Consumers can't set or modify status, so the status of any component doesn't change and status doesn't need to be recalculated at any point.
IComponent
and its implementations (which are subclasses of Component
) are to be used by status producers (status job) and allow setting status. Because they allow setting status, the status of any component can change at any moment. A change in the status of a subcomponent can affect the status of a component that is its root. The subclasses of Component
propagate changes in status differently from subcomponents to root components.
I'll provide some examples for each Component
subclass I have here:
TreeComponent
:
V3 restore is a component with two subcomponents, one for each of its regions--Global and China.
If either Global or China is down, V3 restore is degraded because both Global and China are used by customers to restore using V3.
PrimarySecondaryComponent
:
The gallery is a component with two subcomponents, one for each of its regions--USNC and USSC.
If USNC is up, we don't care about the status of USSC because it's only used if USNC is down.d
LeafComponent
:
The USNC region of Gallery is a LeafComponent
because it can't be broken down into any subcomponents.
(Although TreeComponent
and PrimarySecondaryComponent
both support not having subcomponents and behave as LeafComponent
does, I created this subclass so that status producers don't have to choose between the two arbitrarily.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest adding comments w/ your examples to the code.
public abstract class Component : ReadOnlyComponent, IComponent | ||
{ | ||
public new abstract ComponentStatus Status { get; set; } | ||
public new IEnumerable<IComponent> SubComponents { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there another way to achieve this without the new
to replace base properties? It makes the code more complex.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately no, but I think from a consumer side this makes these classes a lot easier to use because I'm replacing the base properties in an intuitive way. I tried doing this without replacing any properties and it made the code substantially uglier.
I replace Status
to add a setter to a property that is read-only on the base interface.
I replace SubComponents
to provide the additional restriction that all members of the property on the base interface are of a specific subclass (IComponent
instead of IReadOnlyComponent
).
public IEnumerable<IReadOnlyComponent> SubComponents { get; } | ||
public string Path => _parent.Path + Constants.ComponentPathDivider + Name; | ||
|
||
public ReadOnlyComponentWrapper(IReadOnlyComponent component, IReadOnlyComponent parent) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an alternative to wrapper classes for assigning parent/path? Makes the code more complex, and encapsulates the component types that are wrapped.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A component only has a path when accessed as a subcomponent of another component. If we don't wrap or clone the subcomponents, a user could get some very strange results if they reuse the same component in multiple component trees.
In other words:
var a = new Component();
var b = new Component(subcomponents: new[] { a });
var c = new Component(subcomponents: new[] { a });
Console.WriteLine(b.SubComponents.First().Path); // should return "b/a"
Console.WriteLine(c.SubComponents.First().Path); // should return "c/a"
If the constructor of Component
set the path of a
and did not wrap or clone a
, the path set by c
's constructor will override the path set by b
's constructor, which would create unintended results when accessing a
as a subcomponent of b
.
I chose wrapping instead of cloning because I think it would be confusing to have
var a = new Component();
var b = new Component(subcomponents: new[] { a });
a.Status = ComponentStatus.Degraded;
Console.WriteLine(b.SubComponents.First().Status);
// outputs ComponentStatus.Up if cloned
// and ComponentStatus.Degraded if wrapped
Note that this class is also internal and is meant to be invisible to the consumer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assumed a component would only have a single parent, and would either be child or root node. Guess that's not the case?
Maybe Node
would be better suffix than Wrapper
since it implies a relational object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't expect to be creating any components with multiple parents, but I felt this model was easier to use than a model asserting a single parent.
I think I'll add some of the discussion above to a comment in the code to make it clearer. Ultimately, given this is an internal
class, I don't think naming is super important as long as the purpose of the class is communicated well.
public IEnumerable<IReadOnlyComponent> SubComponents { get; } | ||
public string Path => _parent.Path + Constants.ComponentPathDivider + Name; | ||
|
||
public ReadOnlyComponentWrapper(IReadOnlyComponent component, IReadOnlyComponent parent) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assumed a component would only have a single parent, and would either be child or root node. Guess that's not the case?
Maybe Node
would be better suffix than Wrapper
since it implies a relational object?
Part of NuGet/NuGetGallery#6107
These classes will be shared between the Gallery, Status page, and Status Aggregator job to emit and consume status about the service.
I suggest starting at
Status.cs
for the most context.