-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
DocumentMigration: implement downward transformation of documents #153921
DocumentMigration: implement downward transformation of documents #153921
Conversation
…-ref HEAD~1..HEAD --fix'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Self-review
import { TransformSavedObjectDocumentError } from '.'; | ||
|
||
type MigrateAndConvertFn = VersionedTransformer['migrateAndConvert']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was the only place where this function type was used, so I inlined it in VersionedTransformer
instead
@@ -1329,6 +1342,175 @@ describe('DocumentMigrator', () => { | |||
}); | |||
}); | |||
}); | |||
|
|||
describe('down transformation', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most of the unit tests are directly for downgrade_pipeline
. In theory, I should have stubbed the pipeline for the doc migrator tests, but
- we're not doing it for other unit tests in the file and the 'normal' pipeline
- this part is so touchy that including the underlying implementation in the testing makes some sense
So I added some 'integration' smoke tests here.
/* | ||
* This file contains logic for transforming / migrating a saved object document. | ||
* | ||
* At first, it may seem as if this could be a simple filter + reduce operation, | ||
* running the document through a linear set of transform functions until it is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This long wall of text is mostly outdated, and doesn't really bring anything, so I decided to finally get rid of it.
Speak now or forever hold your peace.
migrationVersion: SavedObjectsMigrationVersion; | ||
migrate: MigrateFn; | ||
migrateAndConvert: MigrateAndConvertFn; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
VersionedTransformer
was 'leaking' migrationVersion
and prepareMigrations
that shouldn't be present on that kind of publish-ish interfaces, so I cleaned that up
transformDown: ( | ||
doc: SavedObjectUnsanitizedDoc, | ||
options: { targetTypeVersion: string } | ||
) => SavedObjectUnsanitizedDoc; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: I highly suspect that given what we're intending to use the feature for, we will need some kind of getAtVersion
API that either convert up or down depending on the doc and target version. However it will likely just delegates to the existing APIs, and I didn't want to implement it before having a better understanding of how the SOR will use the feature in #150312
import type { MigrationPipeline, MigrationPipelineResult } from './types'; | ||
import { applyVersion, assertValidCoreVersion } from './utils'; | ||
|
||
export class DocumentDowngradePipeline implements MigrationPipeline { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I spent a lot of time trying to figure out if it was better to have the logic for both up and down conversions at the same place or them being dissociated, and at the end, I think this is better:
- The up migration has a lot of legacy logic because of the
Reference
andConverting
migrations that down migrations don't have to care about - The logic and sanity check are fully different (e.g
assertUpgrade
in the up pipeline) - Even the look and version-applying logic are somewhat different
So I went with the 2 distinct logics approach, and I still think that's the correct decision.
case TransformType.Core: | ||
return ( | ||
this.targetCoreVersion && | ||
(coreMigrationVersion == null || | ||
Semver.gt(coreMigrationVersion, this.targetCoreVersion)) && | ||
Semver.gt(version, this.targetCoreVersion) | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I implemented support for downward core
transforms, even if it's not planned to use it atm.
typeMigrationVersion: this.targetTypeVersion, | ||
...(coreMigrationVersion ? { coreMigrationVersion } : {}), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Different behavior than the up migrations, here we always apply/set the target version to the document before returning it.
migrations: ActiveMigrations; | ||
kibanaVersion: string; | ||
convertNamespaceTypes: boolean; | ||
targetTypeVersion?: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added the targetTypeVersion
option to the up pipeline, as I suspect we will need it. Note that atm, the option is not exposed or used by the document migrator itself.
if (this.document.type !== this.originalDoc.type) { | ||
throw new Error(`Changing a document's type during a migration is not supported.`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is (finally) no longer possible, as it would be a real problem for ZDT.
Note that no one ever used this functionality anyway.
Pinging @elastic/kibana-core (Team:Core) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job! LGTM! I added a few nits for your consideration.
|
||
beforeEach(() => { | ||
const migrate1 = createTransformFn((doc) => { | ||
doc.attributes.wentThoughDown1 = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: wentThroughDown1
(missing r
)
1: { | ||
modelChange: { | ||
type: 'expansion', | ||
transformation: { up: jest.fn(), down: migrate1 }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: Are we planning to add an FTR that checks up & down migrations are symmetric? I guess this is something we'll do in follow-up PRs, right?
P.S.: This comment doesn't belong here... but this line reminded me that...
expect(result).toEqual({ | ||
transformedDoc: resultDoc, | ||
additionalDocs: [], | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: should we check that downTransform
has been called once?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's covered in the previous test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job @pgayvallet ! Left a few comments (mostly for my education).
Note: The feature is currently unused, and will only be when we'll start #150312
👍🏻
id: 'smelly', | ||
type: 'dog', | ||
attributes: { name: 'Callie' }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Smelly Callie 😄
kibanaVersion: this.options.kibanaVersion, | ||
targetTypeVersion: options.targetTypeVersion, | ||
}); | ||
const { document } = pipeline.run(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible for a down migration to create additional docs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In practice, no as the higher-level API does not allow it. I've been reusing the same internal types that the up migration for simplicity's sake.
return transforms; | ||
return Object.entries(modelVersionMap).map<Transform>(([rawModelVersion, definition]) => { | ||
const modelVersion = assertValidModelVersion(rawModelVersion); | ||
const virtualVersion = modelVersionToVirtualVersion(modelVersion); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A little late to the party on this, but would it be possible to get a little bit more context on the exact purpose of a virtual version in the doc comment of modelVersionToVirtualVersion
or modelVersionVirtualMajor
? I understand what it is (thanks to your examples) but I don't fully understand the role it plays, specifically the "virtual" part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid breaking change in the way
- the documentMigration handles versions
- the way versions are stored in
typeModelVersion
in documents
We're considering that model versions are just version 10
. E.g MV 1 = version 10.1.0
. This is just a trick to avoid greatly reduce the internal changes
const latestVersion = this.typeTransforms.latestVersion.migrate; | ||
|
||
if (currentVersion && Semver.gt(currentVersion, latestVersion)) { | ||
throw Boom.badData( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious: is the intention to continue using/proliferating use of Boom
? My understanding is that it is legacy, but that may be mistaken.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to get rid of those atrocities. I just didn't want to do it in the current PR as I suspect some snapshots are based on those. But you're right, for the new pipeline, I can remove them right now. Will do.
@elasticmachine merge upstream |
💚 Build Succeeded
Metrics [docs]Public APIs missing comments
Unknown metric groupsAPI count
ESLint disabled in files
ESLint disabled line counts
Total ESLint disabled count
History
To update your PR or re-run it, just comment with: |
Summary
Fix #150301
Implements
DocumentMigrator.transformDown
and the associated transformation pipeline.Note: The feature is currently unused, and will only be when we'll start #150312