{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/orleans/issues/1305","labels_url":"https://api.github.com/repos/dotnet/orleans/issues/1305/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/orleans/issues/1305/comments","events_url":"https://api.github.com/repos/dotnet/orleans/issues/1305/events","html_url":"https://github.com/dotnet/orleans/issues/1305","id":127541685,"number":1305,"title":"Replace ImmutableAttribute with IImmutable interface","user":{"login":"jason-bragg","id":10675494,"avatar_url":"https://avatars.githubusercontent.com/u/10675494?v=3","gravatar_id":"","url":"https://api.github.com/users/jason-bragg","html_url":"https://github.com/jason-bragg","followers_url":"https://api.github.com/users/jason-bragg/followers","following_url":"https://api.github.com/users/jason-bragg/following{/other_user}","gists_url":"https://api.github.com/users/jason-bragg/gists{/gist_id}","starred_url":"https://api.github.com/users/jason-bragg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jason-bragg/subscriptions","organizations_url":"https://api.github.com/users/jason-bragg/orgs","repos_url":"https://api.github.com/users/jason-bragg/repos","events_url":"https://api.github.com/users/jason-bragg/events{/privacy}","received_events_url":"https://api.github.com/users/jason-bragg/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/dotnet/orleans/labels/enhancement","name":"enhancement","color":"84b6eb"}],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/dotnet/orleans/milestones/10","html_url":"https://github.com/dotnet/orleans/milestones/1.2.0","labels_url":"https://api.github.com/repos/dotnet/orleans/milestones/10/labels","id":1428780,"number":10,"title":"1.2.0","description":"","creator":{"login":"sergeybykov","id":8248806,"avatar_url":"https://avatars.githubusercontent.com/u/8248806?v=3","gravatar_id":"","url":"https://api.github.com/users/sergeybykov","html_url":"https://github.com/sergeybykov","followers_url":"https://api.github.com/users/sergeybykov/followers","following_url":"https://api.github.com/users/sergeybykov/following{/other_user}","gists_url":"https://api.github.com/users/sergeybykov/gists{/gist_id}","starred_url":"https://api.github.com/users/sergeybykov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sergeybykov/subscriptions","organizations_url":"https://api.github.com/users/sergeybykov/orgs","repos_url":"https://api.github.com/users/sergeybykov/repos","events_url":"https://api.github.com/users/sergeybykov/events{/privacy}","received_events_url":"https://api.github.com/users/sergeybykov/received_events","type":"User","site_admin":false},"open_issues":3,"closed_issues":12,"state":"open","created_at":"2015-11-25T07:31:55Z","updated_at":"2016-01-19T21:23:03Z","due_on":null,"closed_at":null},"comments":1,"created_at":"2016-01-19T21:22:53Z","updated_at":"2016-01-19T21:38:21Z","closed_at":null,"body":"The immutable attribute is used to indicate that the data in the class being provided to a grain call will not be modified, so it's safe to pass it without making a copy of the data.  This is a grain call optimization.  There are two ways to mark data as immutable for grain calls.\r\n\r\nImmutable attribute marker\r\n\r\n    [Serializable]\r\n    [Immutable]\r\n    public class ImmutableBlarg\r\n    {\r\n        public int A { get; private set; }\r\n        public ImmutableBlarg(int aval)\r\n        {\r\n            A = aval;\r\n        }\r\n    }\r\n\r\nThe attribute marks the ImmutableBlarg class as immutable so when it's used in a grain call it will not be copied.  This is the preferred method as it is safer, as the type itself enforces the immutability, but it requires that the type be modifiable, so it can't be used if one wants to pass a basic type like byte[] or  third party types.\r\n\r\nGeneric wrapper marker\r\n\r\n    public interface ISomeGrain : IGrainWithIntegerKey\r\n    {\r\n        Task DoSomething(Immutable<Blarg> data);\r\n    }\r\n\r\nThe Immutable<T> generic wrapper indicates that the values in Blarg will not be changed, so it's safe to not make a copy of the object during the call.  This is useful for objects that are not inherently immutable or those who's type information cannot be modified.\r\n\r\nThe use of immutable data is a valuable optimization, but using an attribute is an inefficient approach.  The issue is that getting attributes is one of the more expensive reflection calls, and the use of this attribute means that we must incur this call on every argument on every grain call, regardless if the attribute is use or not.\r\n\r\nI propose replacing the ImmutableAttribute with an IImmutable interface, and use an inheritance check (which is cheaper) instead of an attribute check.\r\n\r\nSomething like:\r\n\r\n    // Immutable marker interface\r\n    public class IImmutable\r\n    {\r\n    }\r\n\r\n    // Use Immutable interface to mark classes as immutable\r\n    [Serializable]\r\n    public class ImmutableBlarg : IImmutable\r\n    {\r\n        public int A { get; private set; }\r\n        public int B { get; private set; }\r\n\r\n        public ImmutableBlarg(int aval, int bval)\r\n        {\r\n            A = aval;\r\n            B = bval;\r\n        }\r\n    }\r\n\r\nThe use of an interface rather than an attribute reduces the overhead of checking for immutable data.  This should result in faster grain calls.\r\n\r\nI'd like to introduce the IImmutable interface and  mark the ImmutableAttribute as deprecated for Orleans 1.2.0 and remove ImmutableAttribute entirely in 1.3.0.\r\n\r\nThoughts/Opinions"},"comment":{"url":"https://api.github.com/repos/dotnet/orleans/issues/comments/172995365","html_url":"https://github.com/dotnet/orleans/issues/1305#issuecomment-172995365","issue_url":"https://api.github.com/repos/dotnet/orleans/issues/1305","id":172995365,"user":{"login":"ReubenBond","id":203839,"avatar_url":"https://avatars.githubusercontent.com/u/203839?v=3","gravatar_id":"","url":"https://api.github.com/users/ReubenBond","html_url":"https://github.com/ReubenBond","followers_url":"https://api.github.com/users/ReubenBond/followers","following_url":"https://api.github.com/users/ReubenBond/following{/other_user}","gists_url":"https://api.github.com/users/ReubenBond/gists{/gist_id}","starred_url":"https://api.github.com/users/ReubenBond/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ReubenBond/subscriptions","organizations_url":"https://api.github.com/users/ReubenBond/orgs","repos_url":"https://api.github.com/users/ReubenBond/repos","events_url":"https://api.github.com/users/ReubenBond/events{/privacy}","received_events_url":"https://api.github.com/users/ReubenBond/received_events","type":"User","site_admin":false},"created_at":"2016-01-19T21:38:21Z","updated_at":"2016-01-19T21:38:21Z","body":"We could cache the result in `IsOrleansShallowCopyable` like we do with non-generic value types, although that might not be faster than using `IImutable` + an assignability check."}}