-
Notifications
You must be signed in to change notification settings - Fork 0
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
Update API to deploy separate endpoints by run ID #2
Update API to deploy separate endpoints by run ID #2
Conversation
api.R
Outdated
} else { | ||
} else if (file.exists("secrets/ENV_FILE")) { | ||
readRenviron("secrets/ENV_FILE") | ||
} else { | ||
readRenviron(".env") | ||
} | ||
readRenviron(".env") |
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.
The logic here was a bit confusing for local development, where neither /run/secrets/ENV_FILE
nor secrets/ENV_FILE
exist. I think the new conditional structure should make local development easier, but let me know if I'm misinterpreting something.
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 agree this is pretty confusing and underdocumented. If I recall, the file secrets/ENV_FILE
is mounted to /run/secrets/ENV_FILE
when using compose. This file contains AWS creds specific to the API. During development, these creds would be unnecessary as the user would likely have active AWS creds via aws-mfa
.
The .env
file is separate and not related. It contains the rest of the setup vars used by compose (API_PORT
, CCAO_REGISTRY_URL
, etc.) and the final model ID and year. In other words, it's just config stuff, not actually secret. This file is necessary to load during development, but NOT when deployed via compose (compose adds all vars in a .env file to the deployed container). So, the logic here makes sense if you remove the change on line 15.
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 makes sense, I refactored in abe5403 to always load this file and only load secrets/ENV_FILE
if it exists (similar to /run/secrets/ENV_FILE
).
api.R
Outdated
api_port <- as.numeric(Sys.getenv("API_PORT", unset = "3636")) | ||
default_run_id_var_name <- "AWS_S3_MODEL_RUN_ID" |
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 might be nice to change the name of this env var to something that more clearly marks it as the default (like AWS_S3_DEFAULT_MODEL_RUN_ID
), but keeping the same name for now means one fewer thing we have to change during deployment. I'm open to changing it now if you feel strongly about it, however.
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.
Let's change it now and make a list of things we actually need to change when redeploying.
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.
Done in abe5403, with an updated list of deploy steps in the PR body.
api.R
Outdated
"2024-02-06-relaxed-tristan", | ||
"2024-03-17-stupefied-maya" |
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.
Any other run IDs that we should include in this vector for now?
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.
Let's actually include the final 2022 and 2023 models. We can just reproduce and replace the old workbooks for those years.
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.
Cool, done in abe5403. Are the old workbooks even still operational, given that the model version in the (currently static) API has changed since 2022 and 2023?
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.
@jeancochrane Like the simplicity here, this looks good but for the env management stuff. Let's add an endpoint for all final models we have the necessary artefacts for, that should be 2022 onward.
We'll also need to update the API workbooks to handle the change (pointing to the endpoint with run_id as a param, rather than the default).
api.R
Outdated
} else { | ||
} else if (file.exists("secrets/ENV_FILE")) { | ||
readRenviron("secrets/ENV_FILE") | ||
} else { | ||
readRenviron(".env") | ||
} | ||
readRenviron(".env") |
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 agree this is pretty confusing and underdocumented. If I recall, the file secrets/ENV_FILE
is mounted to /run/secrets/ENV_FILE
when using compose. This file contains AWS creds specific to the API. During development, these creds would be unnecessary as the user would likely have active AWS creds via aws-mfa
.
The .env
file is separate and not related. It contains the rest of the setup vars used by compose (API_PORT
, CCAO_REGISTRY_URL
, etc.) and the final model ID and year. In other words, it's just config stuff, not actually secret. This file is necessary to load during development, but NOT when deployed via compose (compose adds all vars in a .env file to the deployed container). So, the logic here makes sense if you remove the change on line 15.
api.R
Outdated
api_port <- as.numeric(Sys.getenv("API_PORT", unset = "3636")) | ||
default_run_id_var_name <- "AWS_S3_MODEL_RUN_ID" |
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.
Let's change it now and make a list of things we actually need to change when redeploying.
api.R
Outdated
"2024-02-06-relaxed-tristan", | ||
"2024-03-17-stupefied-maya" |
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.
Let's actually include the final 2022 and 2023 models. We can just reproduce and replace the old workbooks for those years.
docker-compose.yaml
Outdated
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 don't remember why we're running privileged: true
here, but we should try to remove it.
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.
Done in d8bf46f, let's see what happens!
api.R
Outdated
all_endpoints[[i]] <- list( | ||
path = glue::glue("{base_url_prefix}/{run$run_id}"), | ||
model = model, | ||
is_default = run$run_id == default_run$run_id |
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 a similar vein, it would make a lot more sense to just append another entry to all_endpoints
for the default run rather than have to check the is_default
bool in all of the iteration blocks that follow, but I couldn't figure out a good way to do this given the way append operations in R seem to require index references (i.e. the all_endpoints[[i]]
assignment on line 167).
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 should be able to use
all_endpoints <- c(all_endpoints, default_endpoint)`
or:
all_endpoints <- append(all_endpoints, default_endpoint)`
If that simplifies things.
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.
Ready for another look @dfsnow! |
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.
Looks great @jeancochrane. One potential simplification and then this is set to deploy.
api.R
Outdated
all_endpoints[[i]] <- list( | ||
path = glue::glue("{base_url_prefix}/{run$run_id}"), | ||
model = model, | ||
is_default = run$run_id == default_run$run_id |
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 should be able to use
all_endpoints <- c(all_endpoints, default_endpoint)`
or:
all_endpoints <- append(all_endpoints, default_endpoint)`
If that simplifies things.
use_path_in_nav_bar = TRUE, | ||
show_method_in_nav_bar = "as-plain-text" |
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 seems like the rapidoc UI layout changed with the version update, so we need these two params in order to properly display all of the available endpoints (docs).
handler_startup._lgb.Booster <- function(vetiver_model) { | ||
attach_pkgs(vetiver_model$metadata$required_pkgs) | ||
} |
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.
Turns out vetiver_create_description
and vetiver_create_meta
are in fact necessary for calling vetiver_model
on the model, so I added them back in and only removed handler_startup
, which we no longer call after moving from vetiver to raw Plumber.
@@ -1,7 +1,7 @@ | |||
sandbox/ | |||
library/ |
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.
renv/.gitignore
, renv/activate.R
, and renv/settings.dcf
all got automatically updated as part of the upgrade to renv 1.0.x and R 4.4.x.
@dfsnow I think we should get one last look at this, unfortunately I had to make a lot of changes since the last round of review! |
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 work @jeancochrane. I'm excited to see if we can recreate workbooks/create tools to cover all the previous years.
dvc_bucket_pre_2024 <- "s3://ccao-data-dvc-us-east-1" | ||
dvc_bucket_post_2024 <- "s3://ccao-data-dvc-us-east-1/files/md5" |
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.
Whoa, I didn't realize that DVC had changed the file locations :/
default_run_id <- Sys.getenv(default_run_id_var_name) | ||
|
||
# The list of runs that will be deployed as possible model endpoints | ||
valid_runs <- rbind( |
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.
suggestion (non-blocking): It might be worth just updating the model.final_model
dbt seed to capture this list and its attributes. That seed only gets updated at the end of each year, so we could just basically down > up the compose stack to get the new endpoint, rather than modifying the code.
If this sounds like a good idea, let's push it to another separate PR.
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.
Good call, issue opened here: ccao-data/data-architecture#428
See ccao-data/api-res-avm#2 for actual API refactor. Workbooks now select their run ID/target endpoint from cell B1.
See ccao-data/api-res-avm#2 for actual API refactor. Workbooks now select their run ID/target endpoint from cell B1.
This PR updates
api.R
to deploy multiple/predict
endpoints, one for each of a set of valid run IDs. The new API structure looks like this:POST /predict
: Predicts with a default model, configured via theAWS_S3_MODEL_RUN_ID
environment variablePOST /predict/<run_id>
: Predicts with a model determined by the run ID passed to the endpointMaintaining a
POST /predict
endpoint that falls back to a default model should allow us to deploy this change without disrupting the operation of the workbooks that Valuations is currently using for desk review.Closes #1.
Deployment steps
AWS_S3_MODEL_RUN_ID
toAWS_S3_DEFAULT_MODEL_RUN_ID
AWS_S3_DVC_BUCKET
AWS_S3_MODEL_YEAR
privileged: true