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

feat: introducing the fleet package #3105

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

TBonnin
Copy link
Collaborator

@TBonnin TBonnin commented Dec 3, 2024

The fleet package will be used to managed the lifecycle of runners

This commit includes:

  • migration for deployments and nodes tables
  • deployments and nodes models (with tests)

This commit DOESN'T include (I though that would be enough for a first PR):

  • the control loop to handle nodes state transition
  • the routing logic

How to test

The code is not used so it is not possible to run it manually. There is tests though :)

The fleet package will be used to managed the lifecycle of runners
This commit includes:
- migration for deployments and nodes tables
- deployments and nodes models (with tests)
This commit DOESN'T include:
- the control loop to handle nodes state transistion
- the routing logic
@TBonnin TBonnin requested a review from a team December 3, 2024 21:23
Copy link

linear bot commented Dec 3, 2024

Comment on lines 32 to 33
routing_id varchar(255) NOT NULL,
deployment_id char(40) NOT NULL REFERENCES deployments(commit_id) ON DELETE CASCADE,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Need a unique index for those two no?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

indeed

Copy link
Collaborator Author

@TBonnin TBonnin Dec 4, 2024

Choose a reason for hiding this comment

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

I now remember why I didn't make it unique. Because any node can end up in ERROR state and therefore several entry can have the same routingId/deploymentId.
Ex:

  1. Node is created as PENDING
  2. Request to Render to start the new runner fails for some reason. Node is set to ERROR
  3. A new node with same routingID and deploymentId is created as PENDING

Another example: if runner is idled because of inactivity, another runner might potentially be created with the same routingId/deployment

export class Fleet {
private dbClient: DatabaseClient;
constructor({ fleetId, dbUrl }: { fleetId: string; dbUrl: string }) {
this.dbClient = new DatabaseClient({ url: dbUrl, schema: fleetId });
Copy link
Collaborator

Choose a reason for hiding this comment

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

why not passing the DBClient directly?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

because I want the db schema to reflect the name/id of the fleet. I could set the schema for the DBClient and then pass the client but it is more encapsulated this way

Copy link
Contributor

Choose a reason for hiding this comment

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

Are you thinking we would have multiple fleet schemas in production?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

not planning to have multiple fleet anytime soon no but nothing would prevent it in the code

return Err(new Error(`Error: no deployment '${commitId}' created`));
}
return Ok(DBDeployment.to(inserted));
});

This comment was marked as resolved.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

very good point. I forgot about rolling back. commitHash is indeed not unique. I will fix

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure where rolling back is headed, so we may already align, but I think it will be clearer if we're just allowing duplicate commitHash entries with different ids, so that we have a linear history. So a rollback would just be us deploying an old version again, rather than removing anything.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

that's exactly what's this is doing. Deployments are immutable and rolling back means inserting a new entry with the same commitHash. No update, no deletion

return Ok(DBDeployment.to(inserted));
});
} catch (err: unknown) {
return Err(new Error(`Error creating deployment '${commitId}': ${stringifyError(err)}`));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of stringifying you can use new Error(msg, { cause }) which was made for this.
Also could be great to have a dedicated Error type (e.g: FleetError) just for clarity

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

added a FleetError in ee4d0e9 let me know what you think?

storageMb: 512
})
).unwrap();
expect(node).toMatchObject({

This comment was marked as resolved.

return Err(new Error(`Error: no node '${nodeProps.routingId}' created`));
}
return Ok(DBNode.from(inserted[0]));
} catch (err: unknown) {

This comment was marked as resolved.

}
return Ok(DBNode.from(inserted[0]));
} catch (err: unknown) {
return Err(new Error(`Error creating node '${nodeProps.routingId}': ${stringifyError(err)}`));

This comment was marked as resolved.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

you are right. I should try/catch everywhere


const transition = NodeStateTransition.validate({ from: node.value.state, to: props.newState });
if (transition.isErr()) {
return Err(transition.error);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return Err(transition.error);
return transition.error;

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

A result must be returned. I could maybe do return transition.mapError(...) but I don't think it is more readable

return db.transaction(async (trx) => {
const node = await get(trx, props.nodeId, { forUpdate: true });
if (node.isErr()) {
return Err(new Error(`Node '${props.nodeId}' not found`));

This comment was marked as resolved.

@bodinsamuel
Copy link
Collaborator

let me know when you want another pass

@TBonnin
Copy link
Collaborator Author

TBonnin commented Dec 4, 2024

let me know when you want another pass

I have addressed all your comments I think so happy to get another pass

Copy link
Contributor

@nalanj nalanj left a comment

Choose a reason for hiding this comment

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

Pretty cool to see it taking shape. None of the things I commented on were things that I feel too too strongly about, more just asking questions and pondering.

});

describe('deploy', () => {
it('should create a new deployment', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not 100% sure about the right way for us to isolate this, and it's certainly a philosophical thing, but this test effectively runs the same code as the other deployment creation test. It might be a nice spot for us to use a test stub knowing that fleet can trust that deploy is well tested.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You are right. On the other hand stub everywhere could introduce more issues down the line when underlying logic is changed.

I've added this test to bootstrap the fleet.integration.test file but I might remove it since it is not very useful

export class Fleet {
private dbClient: DatabaseClient;
constructor({ fleetId, dbUrl }: { fleetId: string; dbUrl: string }) {
this.dbClient = new DatabaseClient({ url: dbUrl, schema: fleetId });
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you thinking we would have multiple fleet schemas in production?

return Err(new Error(`Error: no deployment '${commitId}' created`));
}
return Ok(DBDeployment.to(inserted));
});
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure where rolling back is headed, so we may already align, but I think it will be clearer if we're just allowing duplicate commitHash entries with different ids, so that we have a linear history. So a rollback would just be us deploying an old version again, rather than removing anything.

Ex:
in case of error, a new node with same routingId/deploymentId will be
created
in case of runner idled because of inactivity, a new node with same
routingId/deploymentId will be created
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

Successfully merging this pull request may close these issues.

3 participants