A branch deployment lets you try changes to features and resolvers without changing your mainline deployment, the deployment that chalk apply creates without the --branch flag. When working in a branch, you can:

  • have Chalk watch your files and deploy any changes
  • deploy updates to your project in seconds
  • adjust and create features and resolvers inside a Jupyter notebook
  • recompute datasets to see the effects of changes
  • keep the same branch name across deployments

A branch does not persist results to the online or offline store, and some settings always come from the mainline deployment. See Branch limitations.


​
Create a branch deployment

To create a branch, run chalk apply --branch <branch_name>. Chalk creates a new branch in the environment that you can interact with. You can then make queries against your branch with chalk query --branch.
If you’re using the Python API, you can set the branch when creating your client, and all subsequent commands will execute against your branch.

The environment needs a mainline deployment before you can create a branch. If you have not deployed to the environment yet, run chalk apply without --branch first.

​
Continuously deploy changes

You can ask the Chalk CLI to continuously deploy changes to your branch using the watch flag. Chalk will automatically keep the branch up to date as you make changes locally.


​
Branch limitations

​
What a branch can change

A branch deployment can add or change:

  • features
  • resolvers, except stream resolvers

Code on a branch can refer by name to data sources and Chalk Catalog entries that already exist. Everything else comes from the active mainline deployment, including:

  • environment variables
  • secrets
  • the Chalk platform version
  • data sources
  • model namespaces in the model registry

To change any of these, run chalk apply without --branch.

​
What a branch cannot do

A branch deployment cannot:

  • write feature values to the online store, so resolvers run on every query even when a feature sets max_staleness,
  • add a new materialized aggregation,
  • write feature values to the offline store,
  • write to the vector store,
  • record feature metrics,
  • add a new data source,
  • add a new model namespace to the model registry,
  • run stream resolvers, or
  • run scheduled resolvers on their schedule.

Chalk still records each query run against a branch, tagged with the branch name.

By default, online queries against a branch read values from the mainline deployment’s online store.

​
Branch online store

To give branches their own online store, go to Infrastructure > Resource Configuration > Branch Server in the dashboard and turn on Deploy Branch Online Store. The branch online store is separate from the mainline deployment’s online store. With it turned on:

  • online queries against a branch read from the branch online store instead of the mainline online store,
  • values you upload to a branch with upload_features are stored for that branch and returned by later queries against it,
  • query results are still not stored, so resolvers run on every query, and
  • Chalk clears the stored values each time you deploy to the branch and each time the branch server restarts.

​
When to use a new branch or a mainline deployment

Each chalk apply --branch <branch_name> replaces the code on that branch. To keep one version running while you try another, deploy the second version to a new branch name. Calling ChalkClient(branch=True) uses the name of your current git branch, with any / replaced by -, which gives each git branch its own Chalk branch.

Run chalk apply without --branch when:

  • production queries need the change,
  • you need feature values written to the online or offline store, for example to backfill a feature or to add a materialized aggregation,
  • you changed an environment variable, a secret, or the platform version,
  • you added a data source or a model namespace, or
  • you need stream resolvers to run, or scheduled resolvers to run on their schedule.

Deploying to mainline does not change your existing branches. Each branch keeps the code you deployed to it, and you do not need to deploy it again. See After a mainline deployment.


​
Develop in a notebook

Once you’ve deployed a branch, you can iteratively edit features and resolvers and see the effects of these updates in a dataset. Create a Chalk client and set the branch parameter equal to your branch. Then, any time you execute a cell that contains a class annotated with @features it will automatically be updated in your branch. Similarly, executing cells that contain a function annotated with @online or @offline will automatically deploy the resolver to your branch.

@features
class NewFeatures:
    id: int
    name: str
    greeting: str

@online
def new_resolver(name: NewFeatures.name) -> NewFeatures.greeting:
    return f"Hello {name}!"

​
Recompute datasets

As you adjust features and resolvers, you can iteratively see how your changes affect your feature values with the Dataset.recompute method. Just pass any features you want to be re-computed as arguments to the features parameter, and Chalk will generate a new Dataset revision using the latest definitions of features and resolvers.

When used in conjunction with the ability to adjust your features and resolvers in the notebook, this tool allows developers and data scientists to rapidly experiment and productionize their work.


​
Example

In the following example we add an oversize feature to an existing dataset of loans.

We start out with a dataset. We may have produced this dataset manually, by calling an external API, or by executing offline_query.

dataset
shape: (786, 3)
┌───────────────┬──────────────┬─────────────────────────┐
│ loan.amount   ┆ loan.id      ┆ loan.event_time         │
│ ---           ┆ ---          ┆ ---                     │
│ f64           ┆ str          ┆ datetime[μs, UTC]       │
╞═══════════════╪══════════════╪═════════════════════════╡
│ 165435.647396 ┆ l_G3Mc6bi9y4 ┆ 2023-04-08 10:03:09 UTC │
│ 405006.796909 ┆ l_O9OK3us7t2 ┆ 2022-02-07 20:46:08 UTC │
│ 680377.427817 ┆ l_L0Gg0Bd1v3 ┆ 2023-02-16 16:48:29 UTC │
│ 562678.545344 ┆ l_D1Rb5Jq4U5 ┆ 2022-06-21 06:15:19 UTC │
│ …             ┆ …            ┆ …                       │
│ 750583.279013 ┆ l_W4ZY9OK5N7 ┆ 2021-12-16 21:40:27 UTC │
│ 71698.15609   ┆ l_H9tG2yJ5B6 ┆ 2023-01-04 11:33:54 UTC │
│ 488697.890372 ┆ l_L4fd4xu4w2 ┆ 2022-08-22 03:55:16 UTC │
│ 769665.198436 ┆ l_k4Dq8bl7b3 ┆ 2022-10-31 07:15:52 UTC │
└───────────────┴──────────────┴─────────────────────────┘

In our Jupyter notebook, we can execute this in a cell to set up our notebook to point to our branch, then update our branch with the new feature and resolver.

client = ChalkClient(branch="<our_branch>")

# We want to introduce an `oversize` detection feature
@features
class Loan:
    id: str
    event_time: datetime
    status: str
    amount: float
    oversize: bool

# Detect any loans bigger than $250,000
@online
def is_oversize(amt: Loan.amount) -> Loan.oversize:
    return amt > 250000

Finally, we can recompute our dataset, telling it to calculate Loan.oversize, to get a dataset back with our new feature values.

# Recomputing the dataset will add the oversize column to our result
dataset.recompute(features=[Loan.oversize])
shape: (786, 4)
┌───────────────┬───────────────┬──────────────┬─────────────────────────┐
│ loan.oversize ┆ loan.amount   ┆ loan.id      ┆ loan.event_time         │
│ ---           ┆ ---           ┆ ---          ┆ ---                     │
│ bool          ┆ f64           ┆ str          ┆ datetime[μs, UTC]       │
╞═══════════════╪═══════════════╪══════════════╪═════════════════════════╡
│ true          ┆ 165435.647396 ┆ l_G3Mc6bi9y4 ┆ 2023-04-08 10:03:09 UTC │
│ true          ┆ 405006.796909 ┆ l_O9OK3us7t2 ┆ 2022-02-07 20:46:08 UTC │
│ true          ┆ 680377.427817 ┆ l_L0Gg0Bd1v3 ┆ 2023-02-16 16:48:29 UTC │
│ true          ┆ 562678.545344 ┆ l_D1Rb5Jq4U5 ┆ 2022-06-21 06:15:19 UTC │
│ …             ┆ …             ┆ …            ┆ …                       │
│ true          ┆ 750583.279013 ┆ l_W4ZY9OK5N7 ┆ 2021-12-16 21:40:27 UTC │
│ true          ┆ 71698.15609   ┆ l_H9tG2yJ5B6 ┆ 2023-01-04 11:33:54 UTC │
│ true          ┆ 488697.890372 ┆ l_L4fd4xu4w2 ┆ 2022-08-22 03:55:16 UTC │
│ true          ┆ 769665.198436 ┆ l_k4Dq8bl7b3 ┆ 2022-10-31 07:15:52 UTC │
└───────────────┴───────────────┴──────────────┴─────────────────────────┘

​
The branch server

All branches in an environment run on one server, the branch server. The branch server is separate from the servers that handle queries against your mainline deployment, and adding branches does not add servers. See Billing for how the branch server is billed.

​
Start and stop the branch server

The branch server does not start when a query arrives. These tools start the branch server if it is not running, and wait for it to be ready before they deploy or query:

  • chalk apply --branch
  • chalk query --branch
  • ChalkClient in chalkpy
  • the query forms in the dashboard

Queries sent over gRPC, for example with ChalkGRPCClient, do not start the branch server. Before you send gRPC queries to a branch, start the branch server with ChalkGRPCClient.await_branch_server_start or one of the tools above.

You can also start and stop the branch server from the Branches page of your environment in the dashboard, with Start Branch Server and Stop Branch Server.

​
Auto-shutdown

The branch server shuts down after a period with no requests. The default period is 1 hour. To change it, go to Infrastructure > Resource Configuration > Branch Server in the dashboard and set Auto Shutdown Period to one of these values:

  • No shutdown
  • 15 minutes
  • 30 minutes
  • 1 hour
  • 2 hours
  • 6 hours

Queries sent over gRPC do not count as activity, so the branch server can shut down while you are sending gRPC queries to it. If you query a branch only over gRPC, set a longer Auto Shutdown Period, or call await_branch_server_start before each set of queries.

​
After a mainline deployment

Each mainline deployment restarts the branch server. Your branches keep the code you deployed to them, and you do not need to deploy them again. Each branch reloads on its first query after the restart, so that query can take around 30 seconds. Later queries return at their usual speed.