Skip to content

convert container-orchestrator to micronaut #19396

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

Merged
merged 60 commits into from
Nov 30, 2022

Conversation

colesnodgrass
Copy link
Member

@colesnodgrass colesnodgrass commented Nov 14, 2022

What

How

  • remove unused dependencies
    • apache-commons
    • jetty
    • config-persistence
    • db-lib
    • protocol-models
    • job-persistence
  • add additional deserialize methods to the Jsons helper class
    • exposes more methods of the underlying jackson object-mapper
      • primarily for avoiding unchecked casts issues
  • rename ContainerOrchestratorApp to Application
  • remove WorkerHeartbeatServer with its HttpServlet
    • replace with Micronaut HeartbeatController
  • combine AsyncStateManager interface with DefaultAsyncStateManager class into AsyncStateManager class
  • move JobOrchestrator classes to a new package
    • replace file reading methods of JobOrchestrator interface with injected dependencies to the implementations
  • add application.yml
  • add micronaut-banner.txt
  • add unit-tests
    • most of existing code did not contain any unit-tests as the code wasn't easily testable, some improvements have been made here and a lot of the new code now has tests
  • general code clean up
    • change visibility of methods to better match their expected usage (mostly moving them from the public scope)
    • remove unnecessary lambda usage (mostly moving to method references)
    • final _type_ -> final var

Oddities

  • the Application class does a little too much currently and should probably be split into two different classes
  • the EventListeners.setEnvVars method should probably be replaced with the env-vars being set on the container itself (in the worker when it creates this pod and containers)
  • the @Secured annotation on the HeartbeatController could (should?) be replaced with something else

@colesnodgrass colesnodgrass temporarily deployed to more-secrets November 14, 2022 21:10 Inactive
@colesnodgrass colesnodgrass temporarily deployed to more-secrets November 15, 2022 04:58 Inactive
@colesnodgrass colesnodgrass temporarily deployed to more-secrets November 29, 2022 21:45 Inactive
@colesnodgrass colesnodgrass temporarily deployed to more-secrets November 29, 2022 21:45 Inactive
Copy link
Contributor

@jdpgrailsdev jdpgrailsdev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@colesnodgrass colesnodgrass temporarily deployed to more-secrets November 30, 2022 18:36 Inactive
@colesnodgrass colesnodgrass temporarily deployed to more-secrets November 30, 2022 19:32 Inactive
@colesnodgrass colesnodgrass merged commit 1cdfdbe into master Nov 30, 2022
@colesnodgrass colesnodgrass deleted the cole/micronaut-container-orchestrator branch November 30, 2022 20:07
@@ -61,6 +61,30 @@ public static <T> T deserialize(final String jsonString, final Class<T> klass) {
}
}

public static <T> T deserialize(final String jsonString, final TypeReference<T> valueTypeRef) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

/**
* Entrypoint for the application responsible for launching containers and handling all message
* passing for replication, normalization, and dbt. Also, the current version relies on a heartbeat
* from a Temporal worker. This will also be removed in the future so this can run fully async.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: I don't think this is right. The heartbeat is what tells the orchestrator to kill itself if Temporal goes down. @benmoriceau @gosusnp what will we replace this with?

}
}

private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fancy!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AKA copy/paste friendly 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed! Wish I had known this earlier.

Comment on lines +77 to +79
asyncStateManager.write(AsyncKubePodStatus.INITIALIZING);
asyncStateManager.write(AsyncKubePodStatus.RUNNING);
asyncStateManager.write(AsyncKubePodStatus.SUCCEEDED, jobOrchestrator.runJob().orElse(""));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to confirm: this is only for tests yes?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

caught my eye because the logic here seems too simple

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. This is the business logic of this code.

How it use to work:

  1. write(INITIALIZING)
  2. block until the configuration files have been copied over via kubectl cp
  3. write(RUNNING)
  4. run job
    5 write(SUCCEEDED)

How it works now:

  1. init-container blocks until configuration files have been copied over via kubectl cp
  2. write(INITIALIZING) // not really necessary any more, as it immediately changes
  3. write(RUNNING)
  4. run job
  5. write(SUCCEEDED)

Copy link
Member Author

@colesnodgrass colesnodgrass Nov 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the setup and configuration code that used to exist and run between INITIALIZING and RUNNING no longer exists as that is now handled by the initContainer and Micronaut.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks for explaining.

feels like INITIALIZING should be moved to the async orchestrator pod process. or removed entirely if we don't think it's useful to track that an attempt was even made.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

at least for me, writing asyncStateManager.write(AsyncKubePodStatus.SUCCEEDED, jobOrchestrator.runJob().orElse("")); is somewhat confusing since it's not immediately clear runJob is blocking.

Would have preferred a more linear style to clarify that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, seems like we could remove it entirely.

Copy link
Contributor

@davinchia davinchia Nov 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thinking more, I feel like it's useful to keep for debugging. would make it obvious if anything crashed between the pod spinning up and actually running. don't feel strongly though

@@ -2,7 +2,7 @@
* Copyright (c) 2022 Airbyte, Inc., all rights reserved.
*/

package io.airbyte.container_orchestrator;
package io.airbyte.container_orchestrator.orchestrator;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate cleaning up the packages!

"image": "airbyte/container-orchestrator:dev-f0bb7a0ba3",
"pullPolicy": "IfNotPresent"
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: newline

port: 9000

airbyte:
config-dir: src/test/resources/files
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: newline

@@ -0,0 +1,31 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for the env testing

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe very nit: these should be sorted for easier reading/future proofing.


@Test
void processFactory() {
assertInstanceOf(KubeProcessFactory.class, processFactory);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting - so this is testing micronaut injects the right class based of env vars?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the idea is to catch an injection issue during tests. This kind of test won't be necessary if we remove all the @Factory classes and make Micronaut manage everything.

Copy link
Contributor

@davinchia davinchia Nov 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kind of like it even if it's fully micronaut managed. We've had several incidents where runtime injection wasn't correct due to either bad runtime dependencies or wrong configuration. Something like this would have help catch it.

@@ -0,0 +1 @@
normalization-orchestrator
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this say container-orchestrator instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, this file tells the container-orchestrator what kind of orchestrator to spin-up

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because we spin up the normalization orchestrator in tests?

@@ -52,15 +54,17 @@ public Class<NormalizationInput> getInputClass() {
@Trace(operationName = JOB_ORCHESTRATOR_OPERATION_NAME)
@Override
public Optional<String> runJob() throws Exception {
final JobRunConfig jobRunConfig = JobOrchestrator.readJobRunConfig();
// final JobRunConfig jobRunConfig = readJobRunConfig();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: remove commented out


public NormalizationJobOrchestrator(final Configs configs, final ProcessFactory processFactory) {
public NormalizationJobOrchestrator(final Configs configs, final ProcessFactory processFactory, final JobRunConfig jobRunConfig) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to this change

@VisibleForTesting
int run() {
// set mdc scope for the remaining execution
try (final var mdcScope = new MdcScope.Builder()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again for understanding, how does this interact with the logging set in EventListeners?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This maintains the existing logging workflow from the pre-micronaut version:

public void run() {
    // this is found in the configureLogging() method, inlined here for ease of reading
    final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
    ctx.reconfigure();
    logClient.setJobMdc(/*...*/);

    // set mdc scope for the remaining execution
    try (final var mdcScope = new MdcScope.Builder()
        .setLogPrefix(application)
        .setPrefixColor(LoggingHelper.Color.CYAN_BACKGROUND)
        .build()) {
    // ...
    }
}

So if the previous version was working as expected, this one will as well. The difference is the configuration of the logger was moved to the EventListener to help keep the main Application class as thin and single-purposed as possible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I'm missing something here then. What is the purpose of configuring the logger in the event listener if the configuration that matters is happening here?

Copy link
Contributor

@davinchia davinchia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the late review @colesnodgrass

Nicely done! Appreciate the general clean up along side the micronaut migration. All round better interface and readability.

Most of my comments are for understanding. I have a few suggestions here and there to improve readability.

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

Successfully merging this pull request may close these issues.

Convert airbyte-container-orchestrator to a Micronaut service
10 participants