-
Notifications
You must be signed in to change notification settings - Fork 443
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
base: master
Are you sure you want to change the base?
Conversation
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
routing_id varchar(255) NOT NULL, | ||
deployment_id char(40) NOT NULL REFERENCES deployments(commit_id) ON DELETE CASCADE, |
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.
Need a unique index for those two no?
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.
indeed
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 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:
- Node is created as PENDING
- Request to Render to start the new runner fails for some reason. Node is set to ERROR
- 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 }); |
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.
why not passing the DBClient directly?
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.
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
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.
Are you thinking we would have multiple fleet schemas in production?
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.
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.
This comment was marked as resolved.
Sorry, something went wrong.
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.
very good point. I forgot about rolling back. commitHash is indeed not unique. I will 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.
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.
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.
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)}`)); |
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.
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
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 a FleetError
in ee4d0e9 let me know what you think?
storageMb: 512 | ||
}) | ||
).unwrap(); | ||
expect(node).toMatchObject({ |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
packages/fleet/lib/models/nodes.ts
Outdated
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.
This comment was marked as resolved.
Sorry, something went wrong.
packages/fleet/lib/models/nodes.ts
Outdated
} | ||
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.
This comment was marked as resolved.
Sorry, something went wrong.
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.
you are right. I should try/catch everywhere
packages/fleet/lib/models/nodes.ts
Outdated
|
||
const transition = NodeStateTransition.validate({ from: node.value.state, to: props.newState }); | ||
if (transition.isErr()) { | ||
return Err(transition.error); |
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.
return Err(transition.error); | |
return transition.error; |
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 result must be returned. I could maybe do return transition.mapError(...)
but I don't think it is more readable
packages/fleet/lib/models/nodes.ts
Outdated
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.
This comment was marked as resolved.
Sorry, something went wrong.
let me know when you want another pass |
I have addressed all your comments I think so happy to get another pass |
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.
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 () => { |
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'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.
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.
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 }); |
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.
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)); | ||
}); |
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'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
The fleet package will be used to managed the lifecycle of runners
This commit includes:
This commit DOESN'T include (I though that would be enough for a first PR):
How to test
The code is not used so it is not possible to run it manually. There is tests though :)