-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
Per discussion with @poikilotherm, opening the first, inaugural issue for some refactoring work prior to the next release being published.
Oliver has already started a branch: main...refactor-for-2.1-release
I will copy-and-paste here the initial discussion on slack. Some potential issues/questions were raised back in the main repo, in the PR where the first iteration of the spi extensions was added: IQSS/dataverse#11767 (comment)
- Make new Context immutable
- Have a complete builder for the Context
- Make the data provider accept a single context object, but keep backward compatibility for using no-arg method from before
- Think about compatibility negotiations for the context with the core: how can an exporter plugin avoid trying to use context options the core doesn't understand? How do we avoid loading an exporter plugin that was compiled against an older version of the SPI than the core uses/supports?
- Idea: let's have a VERSION constant in each interface (exporter and data provider). We increase on any backward incompatible changes (to any interface or the context). In the exporter, we add a default method to ask for the version compatibility, returning it from the class (lookup by constant). The first time we introduce a breaking change to the API (delete methods), we replace the default by forcing everyone to implement. In the core, we check the compatibility and can safely catch any incompatible plugins that still use the old contract. This way we introduce the version level without actually breaking anything now (just mark it deprecated, tell people to implement). Note: this MUST be a constant (public static final) to be put into bytecode AT COMPILE TIME of the plugin. Must not be a method or retrieving from the plugin will have version creep!
-
public interface Exporter { /** * Version of the Exporter interface contract. * Increment when the Exporter interface changes incompatibly. */ int VERSION = 1; /** * Returns the Exporter interface version this plugin was built against. */ default int getExporterInterfaceVersion() { return VERSION; // Inlined at compile time } /** * Returns the minimum ExportDataProvider version required. */ default int getMinimumProviderVersion() { return ExportDataProvider.VERSION; // Inlined at compile time } // ... interface methods } public interface ExportDataProvider { /** * Version of the ExportDataProvider interface contract. * Increment when the provider interface or ExportDataContext changes incompatibly. */ int VERSION = 1; // ... interface methods }
- Why two versions? The SPI has two distinct contract directions:
- Exporter interface — What core expects from plugins. Core calls methods on the plugin's Exporter implementation. If this contract changes (methods added, removed, or signatures changed), core and plugin must agree on the same version.
- ExportDataProvider interface — What plugins expect from core. Plugins call methods on the ExportDataProvider that core provides. If this contract changes, plugins built against an older version may still work (backward compatible), but plugins requiring newer features need a minimum version. The ExportDataContext falls under the same contract (an exporter asks the core within the context), so there is no need for a third version.
- This will and cannot possibly solve the problem of any releases before 6.10 that try to load a plugin with a breaking change. Maybe that's alright, we will just need to have people document this thoroughly. Also, we probably will not see breaking changes soon, so time might be on our side and everybody is on a new enough release by then.
- Think about communicating feedback from the core back to the plugin code: do we keep the exception? Do we want other ways to interact? More explicit exceptions? Capabilities to ask from the provider? A wrapper or hint objects?
- What about pagination with offset / length? What are we actually trying to do here? Some other pattern may be better if we want to iterate through things in chunks (use streaming or a cursor approach), but for one-off stuff to truncate the existing thing is probably fine.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels