-
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
Implement the second part of the ZDT migration algorithm #153031
Implement the second part of the ZDT migration algorithm #153031
Conversation
e2d36fe
to
1646817
Compare
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 @pgayvallet ! This is looking really solid. I left a few comments, no blockers from my side, I am guessing more reviews are in flight 👀 .
One thought is that it would be nice to avoid deleting unknown documents because it should be safe to do leveraging model versions and on-read-migration. I understand maintaining parity with the previous algo is probably still safest but my gut feeling is that discarding data unless absolutely necessary should be avoided.
// couldn't find a way to infer the type of the state depending on the state of the handler | ||
// even if they are directly coupled, so had to force-cast to this ugly any instead. | ||
return stageHandler(current as any, response as any, context); |
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.
Yeah, that is really annoying. One idea, if you specifically want to avoid using any
:
type AnyModelStageHandler = (
state: State,
response: Either.Either<unknown, unknown>,
ctx: MigratorContext
) => State;
then later
const stageHandler = modelStageMap[current.controlState] as AnyModelStageHandler;
Still uses type cast. Happy to stick with what you have + comment.
...cts-migration-server-internal/src/zdt/model/stages/cleanup_unknown_and_excluded_docs.test.ts
Outdated
Show resolved
Hide resolved
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.
❤️ these small files
@@ -48,10 +47,11 @@ export const createTargetIndex: ModelStage<'CREATE_TARGET_INDEX', 'UPDATE_ALIASE | |||
|
|||
return { | |||
...state, | |||
controlState: 'UPDATE_ALIASES', | |||
controlState: aliasActions.length ? 'UPDATE_ALIASES' : 'INDEX_STATE_UPDATE_DONE', |
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.
Forced optimisation :P
.../server/integration_tests/saved_objects/migrations/zero_downtime/conversion_failures.test.ts
Outdated
Show resolved
Hide resolved
await runMigrations(); | ||
}; | ||
|
||
it('should perform a no-op upgrade', async () => { |
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 check my understanding: does the ZDT algo actually re-run queries to check for outdated documents?
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 currently does. it's probably not necessary if we were to check the docVersions
property, but I didn't want to optimize too soon (and checking for docs everytime is safest anyway)
ACK! I'll take a look on Monday |
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.
LGTM! Just added a few minor comments
/** | ||
* Indicates that the algorithm is currently converting the documents. | ||
*/ | ||
convertingDocuments: boolean; |
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 keeping it simple for now. In the future we might want to include a migration_stage
field that claims what step it is. I think it might help with troubleshooting.
*/ | ||
|
||
import { BulkOperationContainer } from '@elastic/elasticsearch/lib/api/types'; | ||
import type { BulkOperation } from '../model/create_batches'; |
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: importing non-common types from the common dir. We should try to avoid that 😇
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.
Yeah... I'm planning on doing some 'move all the things' refactor, but I was planning on waiting until the algorithm is more polished before doing so
const nextTick = () => new Promise<void>((resolve) => resolve()); | ||
const aFewTicks = () => nextTick().then(nextTick).then(nextTick); |
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.
hahaha! the .then
sorting in NodeJS is interesting...
I think the following works and completes immediately because of the fake timers:
it('resolves after the specified amount of time', async () => {
const handler = jest.fn();
const promise = waitForDelay({ delayInSec: 5 })().then(handler);
expect(handler).not.toHaveBeenCalled();
jest.advanceTimersByTime(5000);
await promise;
expect(handler).toHaveBeenCalledTimes(1);
});
@elasticmachine merge upstream |
@elasticmachine merge upstream |
@@ -48,10 +47,11 @@ export const createTargetIndex: ModelStage<'CREATE_TARGET_INDEX', 'UPDATE_ALIASE | |||
|
|||
return { | |||
...state, | |||
controlState: 'UPDATE_ALIASES', | |||
controlState: aliasActions.length ? 'UPDATE_ALIASES' : 'INDEX_STATE_UPDATE_DONE', |
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.
if we create a new index then alias actions should always be empty?
But I wonder why we add aliases as a second step instead of creating the index with the aliases already set (createIndex
accepts aliases
param)?
if (state.newIndexCreation) { | ||
return { | ||
...state, | ||
controlState: 'DONE', | ||
}; | ||
} else { |
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 have tsdocs on state.newIndexCreation but it would be useful to add (another) comment here to explain why we can skip to done
meta: setMetaDocMigrationStarted({ | ||
meta: state.currentIndexMeta, | ||
}), |
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 sometimes awkward in the v2 migration model too :( it's not great that the previous stage needs to know how to prepare the state for the next stage. Almost feels like we want to have two callbacks for each stage in the model, one when entering that state and another when we get an action response. But even if we try this, it feels like the kind of thing we don't want to do inside another PR anyway.
The downside of adding it here is that there's business logic outside of the model and outside the actions making it harder to understand and test. I think we should either
- add more test coverage to next.ts
- Move this into a dedicated
Actions.setDocMigrationStarted
and test it there
I'd slightly lean towards (2) but this does also mean we're putting more business logic into actions them 🤷
...saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.test.ts
Outdated
Show resolved
Hide resolved
packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/types.ts
Outdated
Show resolved
Hide resolved
848bbd5
to
359c64c
Compare
💚 Build Succeeded
Metrics [docs]Public APIs missing comments
Public APIs missing exports
Unknown metric groupsAPI count
ESLint disabled line counts
Total ESLint disabled count
History
To update your PR or re-run it, just comment with: |
Summary
Part of #150309
Follow-up of #152219
Implement the second part of the zero-downtime migration algorithm: the document conversion.
Schema
because a schema is worth a thousand words:
TODO / notepad
check that all types have model versions in INITwill do later when we'll start have real types using MVssupport for coreMigrationVersionadded as a follow-up in the parent issue