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

Allow complex filters for ImplicitStreamSubscriptionAttribute #2988

Merged
merged 7 commits into from
May 10, 2017

Conversation

DixonDs
Copy link
Contributor

@DixonDs DixonDs commented May 1, 2017

My attempt to address #1747

@DixonDs DixonDs force-pushed the implicit-subscription-filter branch from 7dd72e3 to cd06f29 Compare May 1, 2017 13:19
if (!table.TryGetValue(streamNamespace, out entry))
{
entry = FindImplicitSubscribers(streamNamespace);
table[streamNamespace] = entry;
Copy link
Contributor

Choose a reason for hiding this comment

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

Since table is built at runtime, rather than at start, it needs to be a concurrent dictionary, correct? Or is their some other concurrency guarantee that I'm missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jason-bragg Yes, you are right. There was ConcurrentDictionary initially, but then we dealing with some serializer codegen issue I reverted it back to Dictionary and forgot to change it back

{
public interface IStreamNamespacePredicate
{
bool IsMatch(string streamNamespace);
Copy link
Contributor

Choose a reason for hiding this comment

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

If we're going to support custom filtering, I suggest that we widen the filter to include stream full stream id, that is stream guid, namespace, and provider name. Something like:

   bool IsMatch(string streamProviderName, IStreamIdentity streamIdentity);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jason-bragg The only problem with this that it limits the cachebility of the subscribers in ImplicitStreamSubscriberTable since you have to recalculate that list per stream ID + namespace + provider name. With stream IDs having dynamic nature, I guess, it might have significant impact.

What do you think the performance penalty will be in this case? If it is insignificant, I will be happy to make this change.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should be careful here to introduce filtering on streamId into implicit subscribe. Aside from the performance penalty DixonDs brought up, it conflicts with the nature of explicit subscribe too. Because of the dynamic nature of streamId, filtering/subscribe to a stream fits more naturally into the territory of explicit subscribe. Adding the same feature into implicit subscribe may bring more confusion to users , potentially bugs in edge cases. I think we should defer this feature until specifically asked by users

@@ -346,18 +347,26 @@ public LogConsistencyProviderAttribute()
ProviderName = Runtime.Constants.DEFAULT_LOG_CONSISTENCY_PROVIDER_NAME;
}
}

}

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class ImplicitStreamSubscriptionAttribute : Attribute
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest introduction of
abstract class ImplicitStreamSubscriptionBaseAttribute where TPredicate : IStreamNamespacePredicate. new()

with ImplicitStreamSubscriptionAttribute : ImplicitStreamSubscriptionBaseAttribute

So developers can introduce their own attributes with arguments (which simply providing a type does not allow). For instance
RegExImplicitStreamSubscriptionAttribute : ImplicitStreamSubscriptionBaseAttribute with constructors that take a RegEx expression.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jason-bragg I've added another ctor to ImplicitStreamSubscriptionAttribute and removed sealed to allow this kind of extensibility. I didn't go with the abstract base class you suggested, because 1) attributes cannot be generic thus you cannot put generic restrictions on the predicate type like where TPredicate : IStreamNamespacePredicate, new() 2) having a non-abstract attribute with a ctor accepting the predicate type seems to be useful on its own (see FilteredImplicitSubscriptionGrain in tests) so that you don't have to implement your own attribute just to pass the predicate type.

So in the end, it looked like there was no extra benefit of that base attribute type.

@@ -9,10 +10,12 @@ namespace Orleans.Streams
internal class ImplicitStreamSubscriberTable
{
private readonly Dictionary<string, HashSet<int>> table;
private readonly List<Tuple<IStreamNamespacePredicate, int>> predicates;
Copy link
Contributor

Choose a reason for hiding this comment

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

This class needs be serializable. I don't think this change takes that into account.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jason-bragg As far I understand, it should work as long as IStreamNamespacePredicate implementations are serializable. At least it is what I've seen when debugging the tests.

@jason-bragg jason-bragg requested a review from xiazen May 1, 2017 23:39
}

var attribs = grainClass.GetTypeInfo().GetCustomAttributes<ImplicitStreamSubscriptionAttribute>(inherit: false);
var attribs = grainClass.GetTypeInfo().GetCustomAttributes<ImplicitStreamSubscriptionAttribute>(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should be GetCustomAttributes(true) ? instead of false? since user can inherit ImplicitStreamSubscriptionAttribute now?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@xiazen Correct me if I am wrong but inherit: true doesn't have anything to do with the attribute inheritance, but rather the target type inheritance marked by the attribute. GetCustomAttributes<ImplicitStreamSubscriptionAttribute> returns not only ImplicitStreamSubscriptionAttribute but all its descendants - from docs: "The type of attribute to search for. Only attributes that are assignable to this type are returned."

Copy link
Contributor

Choose a reason for hiding this comment

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

I think you are correct. I misread the documentation as

inherit
Type: System.Boolean
true to inspect the descendants of element; otherwise, false. 

The correct doc is true to inspect the ancestors of element; otherwise, false.

private void AddImplicitSubscriber(Type grainClass, ISet<string> namespaces)
/// <param name="streamNamespace">The stream namespace to find subscribers too.</param>
/// <returns></returns>
private HashSet<int> FindImplicitSubscribers(string streamNamespace)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: This method is finding type code of implicit subscribers right? FindImplicitSubscriberTypeCode may be less confusing.

HashSet<int> entry;
return (table.TryGetValue(streamNamespace, out entry) && // if we don't have implictit subscriptions for this namespace, fail out
entry.Contains(grainIdTypeCode)); // if we don't have an implicit subscription for this type of grain on this namespace, fail out
HashSet<int> entry = GetOrAddImplicitSubscribersSet(streamNamespace);
Copy link
Contributor

Choose a reason for hiding this comment

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

same here: GetOrAddImplicitSubscriberTypeCodes will be a less confusing name

return result;
}

private HashSet<int> GetOrAddImplicitSubscribersSet(string streamNamespace)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: This method is get or add type codes of implicit subscribers right? GetOrAddImplicitSubscriberTypeCodes will be a less confusing name

{
public interface IStreamNamespacePredicate
{
bool IsMatch(string streamNamespace);
Copy link
Contributor

Choose a reason for hiding this comment

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

nit on naming : bool Match(string namespace). IsMatch is weird to me grammatically.

Copy link
Contributor

Choose a reason for hiding this comment

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

ignore this... I found out Regex is doing the same naming too. So IsMatch is good with me

{
public interface IStreamNamespacePredicate
{
bool IsMatch(string streamNamespace);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should be careful here to introduce filtering on streamId into implicit subscribe. Aside from the performance penalty DixonDs brought up, it conflicts with the nature of explicit subscribe too. Because of the dynamic nature of streamId, filtering/subscribe to a stream fits more naturally into the territory of explicit subscribe. Adding the same feature into implicit subscribe may bring more confusion to users , potentially bugs in edge cases. I think we should defer this feature until specifically asked by users

namespace Orleans.Streams
{
[Serializable]
internal class ExactMatchStreamNamespacePredicate : IStreamNamespacePredicate
Copy link
Contributor

@xiazen xiazen May 2, 2017

Choose a reason for hiding this comment

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

I think we should also have a RegexStreamNameSpacePredicate

        public class RegexStreamNameSpacePredict
        {
            private readonly Regex regex;

            public RegexStreamNameSpacePredict( Regex regex)
            {
                this.regex = regex;
            }

            bool Match(string streamNameSpace)
            {
                return this.regex.IsMatch(streamNameSpace);
            }
        }

This will be a very common IStreamNamespacePredicate which many users will be interested in

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@xiazen Shall I add also RegexImplicitStreamSubscriptionAttribute in this case since this predicate requires a parameter to be passed?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes I think you are correct! Although I'm not sure it is the correct name.
RegexStreamNameSpacePredict didn't change the nature of ImplicitSubscribe, which is subscribe through annotation on grain class. It just change the underlying mechanism of name space filtering.
But I cannot think of a better name. I guess I'm fine with the name. If you can come up with a better one then it is great!

namespace Orleans.Streams
{
[Serializable]
internal class ExactMatchStreamNamespacePredicate : IStreamNamespacePredicate
Copy link
Contributor

Choose a reason for hiding this comment

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

why is ExactMatchStreamNamespacePredicate internal? it should be public to external users right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@xiazen It is used in ImplicitStreamSubscriptionAttribute, so I didn't see a point to expose it externally, since the users can just use ImplicitStreamSubscriptionAttribute with ctor accepting the stream namespace.

@@ -374,4 +375,13 @@ public ImplicitStreamSubscriptionAttribute(IStreamNamespacePredicate predicate)
Predicate = predicate;
}
}

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class RegexImplicitStreamSubscriptionAttribute : ImplicitStreamSubscriptionAttribute
Copy link
Contributor

@xiazen xiazen May 3, 2017

Choose a reason for hiding this comment

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

Add xml comment. And look through your build warnings to see if it asks you to fill in any other xml comment. Thanks ! Great work !

{
[Serializable]
public class RegexStreamNamespacePredicate : IStreamNamespacePredicate
{
Copy link
Contributor

Choose a reason for hiding this comment

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

Actually I found 0 warnings. But I think it still make sense to add some xml comment on the new predicate and RegexImplicitStreamSubscriptionAttribute . But other than that. I Approve!

Thanks for the work ! It is great

@xiazen
Copy link
Contributor

xiazen commented May 3, 2017

@dotnet-bot test this please

@DixonDs
Copy link
Contributor Author

DixonDs commented May 8, 2017

@jason-bragg is it ready for merging?

@@ -8,11 +10,13 @@ namespace Orleans.Streams
[Serializable]
internal class ImplicitStreamSubscriberTable
{
private readonly Dictionary<string, HashSet<int>> table;
private readonly ConcurrentDictionary<string, HashSet<int>> table;
Copy link
Contributor

Choose a reason for hiding this comment

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

Table is now just a cache, and is dynamically updated at runtime, it no longer should be serialized as part of the ImplicitStreamSubscriberTable.
The predicates are ultimately the authority and the only data that needs serialized.
Or am I missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jason-bragg I guess it depends on what is more efficient - to serialize the cache or to rebuild it on demand. I guess it depends on what predicates are there - since it could be the application code, it is a kind of a black box for us.

But I guess, normally, rebuilding the cache should be faster, so I've done the suggested change.

@jason-bragg
Copy link
Contributor

Sorry for delayed review.. One last thing, then I think it's good.
Thanks a lot!

@jason-bragg
Copy link
Contributor

Thanks @DixonDs
LG2M
Will merge once tests are complete, if @xiazen doesn't beat me to it :)

@sergeybykov sergeybykov added this to the 1.5.0 milestone May 10, 2017
@sergeybykov sergeybykov merged commit 29a24ed into dotnet:master May 10, 2017
@DixonDs DixonDs deleted the implicit-subscription-filter branch May 10, 2017 06:29
@github-actions github-actions bot locked and limited conversation to collaborators Dec 9, 2023
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.

5 participants