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

Find a better type structure #88

Open
Stebalien opened this issue Jun 7, 2018 · 4 comments
Open

Find a better type structure #88

Stebalien opened this issue Jun 7, 2018 · 4 comments

Comments

@Stebalien
Copy link
Member

Currently, we have:

  1. A ton of extension interfaces: CheckedDatastore, ScrubbedDatastore, Batching, etc...
  2. A ton of wrappers: Delayed, Mount, Sync, Autobatch, etc...

And unfortunately, every wrapper needs to implement and forward every extension interface to the underlying datastore. It's kind of a nightmare.

According to @bigs, however,

Typeclasses and modules compose pretty cleanly stuck_out_tongue! Monad transformers exhibit that kind of extensionality pretty well!

I'm not sure about languages like Haskell but I'm pretty sure this can't be done in most languages. However, it's worth discussing as it would be great to have a better system.

Basically, what I want is (pseudo-rust):

trait Datastore { ... }
trait GCDatastore: Datastore { ... }

struct MyDs {
   ...
}

impl Datastore for MyDs { ... }

struct Delayed<D: Datastore> {
  inner: D,
  delay: Duration,
}

impl<D: Datastore> Datastore for Delayed<D> {}

// Can't express this:
forall<X> auto impl X for Delayed<D> where D: X via self.inner;

// That is, auto implement all traits implemented by D on Delayed<D> by proxying to self.inner.

This kind of behavior is usually achieved via the Deref trait. In rust, if a type X implements Deref<Target=SomeOtherType> and someone calls some_x.foo(), the compiler will first try to lookup foo on X, then on SomeOtherType, recursively (if SomeOtherType also implements Deref). However, this doesn't mean that the type X actually implements any of the traits implemented by SomeOtherType.

Thoughts?

@Stebalien
Copy link
Member Author

One solution is to define new types and use go composition:

type Datastore interface {...}
type GCDatastore interface {...}

type MyDs struct {...}
// implement Datastore and GCDatastore for Delayed

type Delayed struct {
  Datastore Datastore
  Delay Duration
}

// Only implement Datastore for Delayed

...

// Compose them:
struct{Delayed, MyDs}{Delayed{Datastore, someDelay}, MyDs}

@bigs
Copy link
Contributor

bigs commented Jun 7, 2018

Yeah, this seems pretty solid. I just kind of wonder if it breaks the facade of our interface altogether... Without generics you're going to have to have a direct reference to the underlying struct unless you're willing to resort to type assignment, which I think is probably OK. Like anything, it will have to be wielded responsibly.

@Stebalien
Copy link
Member Author

By "type assignment" I assume you mean "type assertion" (what it's called in go)? We do that anyways. Basically, I want to be able to take some object of type Datastore (where Datastore is an interface) and cast it to, e.g., GCDatastore).


Unfortunately, my method won't work with, e.g., the mount datastore (or any dispatching datastore, really). This is really where dynamic languages shine.

@guseggert
Copy link
Contributor

This article describes the situation well: https://blog.merovius.de/2017/07/30/the-trouble-with-optional-interfaces.html

I am interested in this because I want to make a dispatching datastore that proxies reqs over a network. With the current type layout, this isn't possible to do dynamically because we can't dynamically define methods. I'll experiment with codegen, which will require generating 2^7=128 different implementations (datastore, batching, scrubbed, checked, persistent, gc, ttl), and would remain backwards compatible.

guseggert added a commit that referenced this issue Mar 23, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher
as on the dispatchee.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement, such as batching. This is
backwards-compatible with existing "features".

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Mar 23, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher
as on the dispatchee. We also want this to be backwards-compatible
with existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement, such as batching. This is
backwards-compatible with existing "features".

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Mar 23, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher
as on the dispatchee. We also want this to be backwards-compatible
with existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement, such as batching. This is
backwards-compatible with existing "features".

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Mar 23, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher
as on the dispatchee. We also want this to be backwards-compatible
with existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature is added, and
then implemented on B, then A will continue to implement the same set
of features since it hasn't added support for the new feature yet (and
vice versa if the feature is implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Mar 23, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher
as on the dispatchee. We also want this to be backwards-compatible
with existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added, and
then implemented on B, then A will continue to implement the same set
of features since it hasn't implemented F yet (and vice versa if F is
implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Mar 23, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher
as on the dispatchee. We also want this to be backwards-compatible
with existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added, and
then implemented on B, then A will continue to implement the same set
of features since it hasn't implemented F yet (and vice versa if F is
implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Mar 23, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher
as on the dispatchee. We also want this to be backwards-compatible
with existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added, and
then implemented on B, then A will continue to implement the same set
of features since it hasn't implemented F yet (and vice versa if F is
implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Mar 23, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher
as on the dispatchee. We also want this to be backwards-compatible
with existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added, and
then implemented on B, then A will continue to implement the same set
of features since it hasn't implemented F yet (and vice versa if F is
implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Mar 26, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher
as on the dispatchee. We also want this to be backwards-compatible
with existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added, and
then implemented on B, then A will continue to implement the same set
of features since it hasn't implemented F yet (and vice versa if F is
implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Mar 26, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher
as on the dispatchee. We also want this to be backwards-compatible
with existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added, and
then implemented on B, then A will continue to implement the same set
of features since it hasn't implemented F yet (and vice versa if F is
implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children
* Communication with data about what functionality a datastore
supports (e.g. for cross-language/RPC support)

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Mar 26, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher
as on the dispatchee. We also want this to be backwards-compatible
with existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added, and
then implemented on B, then A will continue to implement the same set
of features since it hasn't implemented F yet (and vice versa if F is
implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children
* Communication with data about what functionality a datastore
supports (e.g. for cross-language/RPC support)

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Mar 26, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher
as on the dispatchee. We also want this to be backwards-compatible
with existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added, and
then implemented on B, then A will continue to implement the same set
of features since it hasn't implemented F yet (and vice versa if F is
implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children
* Communication with data about what functionality a datastore
supports (e.g. for cross-language/RPC support)

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Mar 26, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher
as on the dispatchee. We also want this to be backwards-compatible
with existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added, and
then implemented on B, then A will continue to implement the same set
of features since it hasn't implemented F yet (and vice versa if F is
implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children
* Communication with data about what functionality a datastore
supports (e.g. for cross-language/RPC support)

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Mar 26, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher
as on the dispatchee. We also want this to be backwards-compatible
with existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added, and
then implemented on B, then A will continue to implement the same set
of features since it hasn't implemented F yet (and vice versa if F is
implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children
* Communication with data about what functionality a datastore
supports (e.g. for cross-language/RPC support)

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Jun 8, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher as
on the dispatchee. We also want this to be backwards-compatible with
existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added,
and then implemented on B, then A will continue to implement the same
set of features since it hasn't implemented F yet (and vice versa if F
is implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children
* Communication with data about what functionality a datastore
supports (e.g. for cross-language/RPC support)

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Jun 8, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher as
on the dispatchee. We also want this to be backwards-compatible with
existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added,
and then implemented on B, then A will continue to implement the same
set of features since it hasn't implemented F yet (and vice versa if F
is implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children
* Communication with data about what functionality a datastore
supports (e.g. for cross-language/RPC support)

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Jun 12, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher as
on the dispatchee. We also want this to be backwards-compatible with
existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added,
and then implemented on B, then A will continue to implement the same
set of features since it hasn't implemented F yet (and vice versa if F
is implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children
* Communication with data about what functionality a datastore
supports (e.g. for cross-language/RPC support)

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Jun 12, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher as
on the dispatchee. We also want this to be backwards-compatible with
existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added,
and then implemented on B, then A will continue to implement the same
set of features since it hasn't implemented F yet (and vice versa if F
is implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children
* Communication with data about what functionality a datastore
supports (e.g. for cross-language/RPC support)

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Jun 12, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher as
on the dispatchee. We also want this to be backwards-compatible with
existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added,
and then implemented on B, then A will continue to implement the same
set of features since it hasn't implemented F yet (and vice versa if F
is implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children
* Communication with data about what functionality a datastore
supports (e.g. for cross-language/RPC support)

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Jun 12, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher as
on the dispatchee. We also want this to be backwards-compatible with
existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added,
and then implemented on B, then A will continue to implement the same
set of features since it hasn't implemented F yet (and vice versa if F
is implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children
* Communication with data about what functionality a datastore
supports (e.g. for cross-language/RPC support)

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Jun 13, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher as
on the dispatchee. We also want this to be backwards-compatible with
existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added,
and then implemented on B, then A will continue to implement the same
set of features since it hasn't implemented F yet (and vice versa if F
is implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children
* Communication with data about what functionality a datastore
supports (e.g. for cross-language/RPC support)

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Jun 13, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher as
on the dispatchee. We also want this to be backwards-compatible with
existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added,
and then implemented on B, then A will continue to implement the same
set of features since it hasn't implemented F yet (and vice versa if F
is implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children
* Communication with data about what functionality a datastore
supports (e.g. for cross-language/RPC support)

Some related issues:

* #160
* #88
guseggert added a commit that referenced this issue Jun 24, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher as
on the dispatchee. We also want this to be backwards-compatible with
existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added,
and then implemented on B, then A will continue to implement the same
set of features since it hasn't implemented F yet (and vice versa if F
is implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children
* Communication with data about what functionality a datastore
supports (e.g. for cross-language/RPC support)

Some related issues:

* #160
* #88
Jorropo pushed a commit that referenced this issue Jun 25, 2022
The motivation for this is to enable "dispatching" datastores that
dynamically implement the type of the datastore they are dispatching
to, so that type assertions behave equivalently on the dispatcher as
on the dispatchee. We also want this to be backwards-compatible with
existing code using type assertions.

At a high level, this works by generating a concrete implementation of
every possible combination of "features", and then picking the right
implementation at runtime. This is necessary due to language
constraints in Go--it is currently impossible to create a concrete
type dynamically with reflection that implements an interface.

"Features" are introduced here, which are supplemental, optional
interfaces that datastores may implement. These are
backwards-compatible with existing "features", which are:

* Batching
* CheckedDatastore
* GCDatastore
* PersistentDatastore
* ScrubbedDatastore
* TTLDatastore
* TxnDatastore

New features can also be added in a backwards-compatible way. E.g. if
datastore A is scoped down to datastore B, a new feature F is added,
and then implemented on B, then A will continue to implement the same
set of features since it hasn't implemented F yet (and vice versa if F
is implemented on A but not B).

Examples of things this enables:

* Allow us to deprecate ErrBatchUnsupported
* Allow existing dispatching datastores to support all
features (keytransform, retrystore, MutexDatastore, autobatch, etc.)
* Features supported by a Mount datastore could be scoped down to the
intersection of all children
* Communication with data about what functionality a datastore
supports (e.g. for cross-language/RPC support)

Some related issues:

* #160
* #88
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants