4.x to 5.0 Migration Guide¶
This page outlines the major changes introduced in 5.0, and how developers should update their apps when transitioning from 4.x to 5.0. A full feature list is available in the 5.0 release notes.
Redirections¶
Full use of the redirect behaviour requires changes in both the node configuration (by the operator) and the endpoint definitions (by the application developer). If redirections are enabled on a node or service by the operator without any change to the application, then all endpoints will revert to their default redirect behaviour, which is that all requests are redirected to the primary. If the endpoint definitions are updated, but then deployed on an in instance with no per-node redirection configuration, then no redirects will be returned (and the service will instead rely on the previous forwarding behaviour).
Forwarding will be deprecated and removed in a future release, so we recommend that all users update their apps and deployments to use redirections.
Warning
While most HTTP client libraries will allow you to automatically follow redirects, many will also remove Authorization
headers after redirection, to prevent you submitting confidential information to an unintended host. Some will only do this if they believe the redirection has crossed to a fresh domain, while others will do it for all redirections.
If your client is submitting an Authorization
header (eg - a JWT Bearer token) yet receiving 401 Unauthorized
responses, it is likely that your HTTP middleware is removing this header on redirect. To correct this you may need to disable automatic redirect following, and instead manually intercept all redirect responses to follow the redirect in your own code, without stripping headers (following some check that the redirection is still to the intended CCF service, eg - to the same origin when ignoring subdomain).
Node configuration¶
Redirects are enabled for each RPC interface by adding a redirections
object to the interface’s JSON configuration. Interfaces without this object will follow use each endpoint’s forwarding properties, to decide whether each request should be executed locally or forwarded, whereas interfaces with this object will use each endpoint’s redirection properties, to decide whether each request should be executed locally or return a redirect header.
Example configuration, redirecting directly to the current primary’s accessible name:
{
"network": {
"node_to_node_interface": { "bind_address": "127.0.0.1:8081" },
"rpc_interfaces": {
"interface_name": {
"bind_address": "127.0.0.1:8080",
"published_address": "ccf.example.com:12345",
"redirections": {
"to_primary": {
"kind": "NodeByRole",
"target": { "role": "primary" }
}
}
}
}
}
}
Example configuration, redirecting to a static address (such as a load balancer):
{
"network": {
"node_to_node_interface": { "bind_address": "127.0.0.1:8081" },
"rpc_interfaces": {
"interface_name": {
"bind_address": "127.0.0.1:8080",
"published_address": "ccf.example.com:12345",
"redirections": {
"to_primary": {
"kind": "StaticAddress",
"target": {
"address": "primary.ccf.example.com"
}
}
}
}
}
}
}
Endpoint definitions¶
Similar to the forwarding_required
property which specified each endpoint’s forwarding behaviour, we introduce a redirection_strategy
property to control the redirection behaviour. This is set by a method on the endpoint in C++:
make_endpoint(...)
...
.set_redirection_strategy(RedirectionStrategy::None)
...
.install()
And a field on the endpoint in JS’s app.json
:
{
"endpoints": {
"/foo/{bar}": {
"get": {
...
"redirection_strategy": "none",
...
}
}
}
}
The default value for both is ToPrimary
/"to_primary"
, meaning that all requests will be redirected. We recommend setting intended values with the following mapping, based on the previous forwarding value, to get similar behaviour in both schemes.
|
|
|
---|---|---|
|
|
|
For read-only operations |
|
|
For write operations |
|
|
|
|
While Never
and Always
have clear analogs in redirection, the session consistency-preserving Sometimes
value is more complicated. All writes should be redirected to a primary, as attempting to execute them on a backup will result in an error. For reads, you may choose to redirect to retain simple consistency, but to support scaling (by reading on backups), we recommend you choose None
for redirections. Where between-request consistency is a strong requirement, we recommend you enforce it at the application level (eg - ETags, request IDs, etc).
ToBackup¶
A third RedirectionStrategy
exists named ToBackup
(represented by "redirection_strategy": "to_backup"
in app.json
). This is a mirror of the ToPrimary
strategy - if such a request is processed by a node which is currently a primary, that node will produce a HTTP redirect response directing to a backup node. The choice of backup is arbitrary. The redirection address which is inserted can be configured per-node by the operator, in the redirections.to_backup
object.
For example, to redirect directly to a backup by their unique accessible hostname:
{
"network": {
"rpc_interfaces": {
"interface_name": {
"redirections": {
"to_backup": {
"kind": "NodeByRole",
"target": { "role": "backup" }
}
}
}
}
}
}
To redirect to a static address (such as a load balancer):
{
"network": {
"rpc_interfaces": {
"interface_name": {
"redirections": {
"to_backup": {
"kind": "StaticAddress",
"target": {
"address": "backup.ccf.example.com"
}
}
}
}
}
}
}