-
Notifications
You must be signed in to change notification settings - Fork 194
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
Getting batched events #205
Conversation
143254f
to
b1e7aa4
Compare
Was this replaced by the other PR? Should it be closed? |
7900f7d
to
5c6d9d7
Compare
extensions/Worker.Extensions.EventHubs/EventHubTriggerAttribute.cs
Outdated
Show resolved
Hide resolved
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 looks great! Awesome set of tests.
Leaving an initial set of comments, but some things may require a chat
extensions/Worker.Extensions.EventHubs/EventHubTriggerAttribute.cs
Outdated
Show resolved
Hide resolved
extensions/Worker.Extensions.EventHubs/EventHubTriggerAttribute.cs
Outdated
Show resolved
Hide resolved
src/DotNetWorker/Context/Features/GrpcFunctionBindingsFeature.cs
Outdated
Show resolved
Hide resolved
namespace Microsoft.Azure.Functions.Worker.Converters | ||
{ | ||
// Converting IEnumerable<> to Array | ||
internal class EnumerableConverter : IConverter |
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.
Adding some suggestions to the class, but given the fact this is pretty specific to gRPC, I'm wondering if this shouldn't be a converted injected by the gRPC integration (mostly thinking about when we break it off) and operating on the gRPC types, which would make this a bit easier and rely less on type checking. We can chat, but wanted to share this to get your thoughts.
extensions/Worker.Extensions.EventHubs/EventHubTriggerAttribute.cs
Outdated
Show resolved
Hide resolved
EnumerableTargetType? targetType = null; | ||
target = null; | ||
// Array | ||
if (context.Parameter.Type.IsArray) |
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.
Use ElementType
or GetElementType
API
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.
Add cache on function id and parameter (or add a TODO)
…s collections event hubs e2e initial updates temp add to solution too update to new model rebase to latest Improved logic for resolving generics remove webjobs reference Fix IEnumerable some enumerable converter tests Clean up test Cleanup reverting local settings json change change localsettings typo added datatype string to some tests fix generator test
2f627df
to
8d0d9a1
Compare
namespace Microsoft.Azure.Functions.Worker | ||
{ | ||
[AttributeUsage(AttributeTargets.Property)] | ||
public class DefaultValueAttribute : Attribute |
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 is temporary to set default while we do not load attributes.
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.
nit:No need to change for right now, but I wonder if we should move this to a different namespace so it's not discoverable and polluting?
LoadConstructorArguments(properties, attribute); | ||
LoadDefinedProperties(properties, attribute); | ||
|
||
return properties; | ||
} | ||
|
||
private static void LoadConstructorArguments(IDictionary<string, object> properties, CustomAttribute attribute) | ||
private static IEnumerable<(string, CustomAttributeArgument?)> GetDefaultValues(this CustomAttribute attribute) |
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 is all should be removed later... after above todo is done: avoid needing to instantiate any types, assume that the constructor argument names are equal to property names.
// Note that this logic doesn't dictate what we can/can't do, and | ||
// we can be more restrictive in the future because today some | ||
// scenarios result in runtime failures. | ||
if (IsIterableCollection(parameterType, out DataType dataType)) |
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.
Note that this should be changed, but likely for a future iteration it will be a runtime failure anyways, so no worries about introducing breaking behavior (it'll just fail faster)
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.
Actually - something that is derived from IEnumerable<Poco>
(ex: List<Poco>
) is valid for cardinality: many and does have an appropriate converter. Would not want to throw in these cases.
The only failure cases are when element type is string
, byte[]
, ReadOnlyMemory<byte>
int
, double
, long
AND collection type is not equal to IEnumerable, IList, or ICollection (or corresponding generic).
We could go one step further and say if not derived from IEnumerable assume it's not batched... in this case we want to know if it was set explicitly or if it's just from the user... for event hubs we definitely want to discourage non-batched anyways
|| elementType.IsAssignableFrom(typeof(byte[])) | ||
|| elementType.IsAssignableFrom(typeof(ReadOnlyMemory<byte>)) | ||
|| elementType.IsAssignableFrom(typeof(long)) | ||
|| elementType.IsAssignableFrom(typeof(double))) |
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.
Note: should improve efficiency here... if anyone has quick suggestions they would be very welcome but otherwise i think i'll flag this as a future improvement
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.
Added some comments, but nothing major. We should be able to get this in as I don't see major blockers, but some of the comments would be good to address.
} | ||
|
||
// IEnumerable and not string or dictionary | ||
bool isEnumerableOfT = IsOrDerivedFrom(type, Constants.IEnumerableOfT); |
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.
nit: Since this may not be used until you reach the last check, could we defer this check?
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 a fancy .net thing i can do to assign while being evaluated (like if (bool t = ShouldContinue())
? I use isEnumerableOfT
a bit earlier in line 473 too
namespace Microsoft.Azure.Functions.Worker | ||
{ | ||
[AttributeUsage(AttributeTargets.Property)] | ||
public class DefaultValueAttribute : Attribute |
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.
nit:No need to change for right now, but I wonder if we should move this to a different namespace so it's not discoverable and polluting?
…en't passed over by host yet apparently
…e) less discoverable
Looks like |
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.
Looks great! All these tests make it so much easier. Thank you!!
I added very minor final nit comments, that don't need to be addressed in this PR. The only thing I will leave it to you is to change the namespace Microsoft.Azure.Functions.Worker.Annotations
return IsDerivedFrom(baseType, interfaceFullName); | ||
} | ||
|
||
private static string? ResolveIEnumerableOfTType(TypeReference type, Dictionary<string, string> foundMapping) |
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.
For later -- If I am reading it right, this finds IEnumerable<T>
through recursion, while IsOrDerivedFrom(..., Constants.IEnumerableOfT
checks if there's any. They both seem to go through all interfaces directly or indirectly implemented through baseType. Is there a reason to do both? Could you combine the two to be like TryGetIEnumerableOfTType
? That way we don't have to recurse the same path twice?
I understand that you may still want IsOrDerivedFrom
to be around for other IEnumerable types.
Definitely not required right now. Was just curious and wanted to make sure I am understanding it right.
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.
Definitely a good call!!
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.
We should flag some of the logic using mono cecil for review. I noticed a few other things we may want to reevaluate, but these are much simpler to walk through with the tests.
TypeDefinition definition = type.Resolve(); | ||
if (definition.HasGenericParameters && type is GenericInstanceType genericType) | ||
{ | ||
for (int i = 0; i < genericType.GenericArguments.Count(); i++) |
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.
For later -- Would it ever make sense to just look at the first argument, as we know IEnumerable<T>
would always only have at most one generic arg?
Also this can be made foreach
. As apparently, ElementAt()
could go over the whole ienumerable every lookup depending on the implementation it says. Not that it matters much here :)
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 done because you might have some Foo<K, V> : IEnumerable<V>
So in this case the recursive search would want to match V from Foo (the second generic) with V from IEnumerable (the first and only generic)
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.
Let's follow up on this case.
extensions/Worker.Extensions.Abstractions/DefaultValueAttribute.cs
Outdated
Show resolved
Hide resolved
|
||
private static object? GetBinaryData(IEnumerable<ReadOnlyMemory<byte>> source, Type targetType) | ||
{ | ||
if (targetType.IsAssignableFrom(typeof(ReadOnlyMemory<byte>))) |
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.
Had left an earlier comment about the comparison using IsAssignableFrom, but can't find it. This check here isn't required, are any of the tests specifically testing this code path? Aside from ReadOnlyMemory<byte>
this shouldn't evaluate to true, in practice.
Resolves #127
Resolves #115
SDK Changes
string
orbyte[]
IBatchedOutput
, convert IsBatched property to add "cardinality": "many" or "cardinality": "one".string
orbyte[]
.NET Worker Changes
RepeatedCollection<string>
,RepeatedCollection<byte[]>
,RepeatedCollection<ReadOnlyMemory<byte>>
,RepeatedCollection<double>
, andRepeatedCollection<long>
(from above capability) to corresponding Array types.E2E App Changes