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.

forwarding_required (C++ / JS)

redirection_strategy (C++ / JS)

Never / "never"

None / "none"

For read-only operations

Sometimes/ "sometimes"

None / "none"

For write operations

Sometimes / "sometimes"

ToPrimary / "to_primary"

Always / "always"

ToPrimary / "to_primary"

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).