-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Storage providers contract is hard to test and tightly coupled with Orleans runtime. #2115
Comments
public interface IGrainIdentity
{
Guid PrimaryKey { get; }
long PrimaryKeyLong { get; }
string PrimaryKeyString { get; }
string IdentityString { get; }
long GetPrimaryKeyLong(out string keyExt);
Guid GetPrimaryKey(out string keyExt);
} Information about which of those properties will throw and which will not throw is unknowable with that interface alone - maybe we should change that and then refactor the provider interfaces to leverage that instead. |
Also to add a bit - it'd be nice if Storage provider can access the data to original state type so we can avoid coupling code with data (see my comment on #2105 for details). |
@ReubenBond this why it'd be nice to simplify it down to a single signature interface. String is pretty ubiquitos. As well as access to original Grain type is required - different grain types may use same state objects, especially if it's some kind of a generic structure |
I think your observation is correct - grain references aren't very test friendly. One complication is that they are expected to be produced by I think to make this functionality properly testable we might need to make type resolution injectable and |
I'm not sure I understand how injectable GrainClient would help with this, can you please elaborate on this ? Technically provider needs these details only to map a request to a single possible stored data entry, which is expected to be of a particular serialized type - this can be done by a very specific interface for storage contract - |
I meant that to test the current provider interface one needs to be able to construct correct grain references, which is only possible today via |
Do we really need a correct Actually this is the question about some plans for providers 2.0 - are they planned to be more knowledgeable about the context they are being run in (know the type of the state, be aware about concept of the grain etc) or they are going to delegate certain hooks to grains so we can have methods invoked by provider on the grain instance ? |
@centur Some information quickly, I had this problem while working with AdoNetStorageProvider. Specifically AdoNetStorage provider uses this algorithm to extract the primary key from As noted, the AdoNetProvider is tested currently differently than the others. A large part of it is that I tried to move to a direction that makes it easier to providers independent of Orleans, but also make testing storage providers easier in general. If you are interested, the higher level description on how I solved is here (look Testing). Specifically it might be possible you take take a module/subtree dependency on Orleans and use the test sets and utilities in the test project and do the following: Copy-paste SqlServerStorageTests and rename appropriately. You don't have to inherit from As I see it, the problem in the current test setup is that that test classes do not set up their own invariants only, but also that of the environment. It leads to tighter decoupling that should be needed and hence makes it more difficult to work on the direction of the testkit. <edit: Writing while running, but just wanted to note I felt I got closer to independence, but not all the way. Ran out of time etc. The referenced AdoNetProvider thread has also some notes that could go to Storage Provider 2.0. <edit 2: The SQL Server and MySQL tests use CommonStorageTests underneath, the rest is just XUnit.NET specific parts using theories and data sets, so the core tests are independent of the testing framework (or almost, some used in asserts, but not mandatory). |
@veikkoeeva I see you've abstracted it and inside your provider you operates only with your domain class, not using grainReference - this is cool, GrainTypeGenerator - it's what I hope to avoid - with the same result I can extract and copy implementation from GrainReference.ToKeyString and mimic string generation in the way that they will be parseable by FromKeyString() implementation. Your PS: My issue here is not in setting up the test harness - it's already there, with fixtures etc, but in a stable way to generate test input without getting into the implementation depths. |
@centur We should start and issue on storage providers 2.0 and see what should go inside of them. Alas, I'm currently too thin on time to write a coherent and a thoughtful ticket (but I will if no one gets there first). One part of discussion could be a set of XUnit.NET class generators and a "core tests", which after implementing one could be assured the provider is "Orleans certified". Perhaps even so that popular ones would be tested as part of Microsoft stress test suite (written in certain way, so could be plugged in and setup done in certain way, such as giving storage string in uniform way). A second part is that it would be beneficial for the application consumer to have a hold on the concrete implementation either when creating one, inside bootstrapper or via the management interface (or all of them). This way storage specific features could be exploited. A third part is that we could perhaps decouple (de)serialization logic from the storage providers as done already in the ADO.NET one. It decouples a lot of things already, not only (de)serialization. By introducing canonical interfaces we could, I think, decouple all serialization logic and have DI just to inject them on whatever basis. Perhaps this could be done with a more generalized interceptor pipeline that is applied to multiple points in Orleans codebase. I had to resort to what I did since there isn't DI yet. :) |
And DI there to instantiate it |
Just edited my previous one to make a note about DI. Constructor injection on storage provider would be handy. Though the <small>I just moved the energy inside me, work calls... </small> |
I see two mostly independent efforts here:
For 1, I believe we do need to test with correct grain references. Otherwise, how do we know the provider will handle them correctly? |
This issue has been marked stale for the past 30 and is being closed due to lack of activity. |
This is an issue as a follow up of two things - our attempt to implement own "safe" blob provider (lil bit incompartible with an existing one) and the discussion with @ReubenBond about design principles and
IStorageProvider
interface's public contract. So may not be always coherent :).The problem is that
IStorageProvider
takes hard dependency onGrainReference
class which, based on it's implementation and visibility considered pretty internal and coupled to Orleans Runtime.I can see 2 problems with this interface
Logger
is a real class, not an abstraction ( but that's fine, it's 'pluggable' so one can fake it easy)GrainReference
is tightly coupled. This is a bigger bummer.When we tried to unit test StorageProvider in isolated environment (with real Azure backed storage but outside of Orleans Runtime) - we can't construct GrainReference intance.
Moreover - to test it properly, we need to construct GrainReference with a certain properties - so we can test interactions of our Storage Provider implementation with this particular instance.
There is no other valid way to construct it, via from
public static GrainReference FromKeyString(string key)
- but this is a chicken and egg problem - we need a valid key or at least an insight on how to make it (internal implementation details from instance methodGrainReference.ToKeyString()
From the perspective of StorageProvider - these details are unnecessary, and Storage provider needs only a way to get some metadata about the Grain - Grain real type, unique ID that is stable for this grain type and maybe something else.
Wouldn't it be better to change (this is a breaking change) this type to an interface which provides access to this information about the grain instance ? E.g.
These parts are from the discussion (don't want to re-type it)
Reuben Bond >Orleans could provide utils for mocking that type?
Reuben Bond> rather avoid that if possible
Reuben Bond> I think it would be better to just give it a protected default constructor
Reuben Bond> and to make the public methods virtual. Or extract a limited interface from it. Or (probably better) refactor that code to use a GrainId and expose GrainId
Reuben Bond > Yeah, the point of that parameter is the grain id anyhow, so either we should provide that as a string or as a GrainId class I'm all for changing things, especially for 2.0.
Any thoughts ? Ideas ? Opinions ?
The text was updated successfully, but these errors were encountered: