Skip to content

RFC: View routing rules #19097

@mhamza15

Description

@mhamza15

Background

Routing rules

Vitess supports routing rules for tables, allowing queries targeting one table in a keyspace to be transparently routed to another table in another (or the same) keyspace. This is used during MoveTables workflows for example, to shift traffic between keyspaces as tables are moved.

In order to support moving views in MoveTables, Vitess needs support for routing rules for views.

Views

Views in Vitess are not explicitly defined in the VSchema. VTGates discover views dynamically through schema tracking, and are stored in the VTGate's local VSchema. When a view changes, VTTablets notify VTGates through healthchecks, which VTGate then fetches the views' definitions with the GetSchema RPC.

At query time, when a VTGate encounters a view, the view is replaced with its definition as a derived table. For example, for this view:

create view user_view as select id, name from user

A query like:

select * from user_view

Would be rewritten to:

select * from (select id, name from user) as user_view

See the Views RFC for more information.

Proposal

View routing rules can be added in the same way as table routing rules:

{
  "rules": [
    {
      "from_table": "my_view",
      "to_tables": ["target_ks.my_view"]
    }
  ]
}

When VTGates are building the routing rules, they'll first look up the target to see if it's a view. If the view does not exist, it'll treat it as a table as normal. If the view does exist, it'll store the routing rule as a new ViewRoutingRule:

type ViewRoutingRule struct {
	TargetKeyspace string
	TargetViewName string
}

For example, the rule shown above would be stored as:

ViewRoutingRule{
  TargetKeyspace: "target_ks",
  TargetViewName: "customer_view"
}

The view routing rules would then be stored in a new map within the VTGate's VSchema:

type VSchema struct {
  // ...

	RoutingRules map[string]*RoutingRule `json:"routing_rules"`

	ViewRoutingRules map[string]*ViewRoutingRule `json:"view_routing_rules"`
    
  // ...
}

Note

The reason we check if it's a view before we check if it's a table is because tables that do not exist in the VSchema of an unsharded keyspace are assumed to be a table regardless. If we check for the reference as a table first, we would always treat it as a table even if there might exist a view with the same name.

Since views are discovered dynamically, the view might not yet exist in the target keyspace's (VTGate-local) VSchema. For example, in the case of a theoretical MoveTables with views:

  1. MoveTables create initiated with view customer_view

  2. customer_view is created in the target keyspace

  3. Before VTGate is aware this view now exists in the target keyspace, MoveTables creates the routing rules

  4. The VTGate receives notice of the new routing rules, but can't find a view (or table) with the same name in the target keyspace. It stores an error in its local routing rules.

    {
      "routing_rules": {
        "customer_view": "table customer_view not found"
      }
    }
  5. Soon after, the schema tracker is now aware of the new view, but the routing rules are not updated.

To work around this, whenever new or updated views are added to the VSchema by the schema tracker, VTGate will rebuild the routing rules so that they can consider the new views.

Query routing

At query time, views are currently replaced with their definition as a derived table. We can update this logic to first look up the view routing rules. If a matching routing rule is found, replace the view with the target view's definition instead. For example, in keyspace source_ks and target_ks, the same view is created:

create view user_view as select id, name from user

This routing rule:

{
  "rules": [
    {
      "from_table": "user_view",
      "to_tables": ["target_ks.user_view"]
    }
  ]
}

Would cause this query:

select * from user_view

To be rewritten to:

select * from (select col1, col2 from target_ks.user_view) as user_view

...rather than ...from source_ks.user_view.

Alternatives

  • Since queries containing a view are rewritten with their definition, we could choose to rely on the underlying tables' routing rules instead of creating explicit routing rules for the views themselves. However, in the case of a MoveTables, this would cause an ambiguous table reference when using global routing, as the view would exist in both keyspaces. Therefore, we require explicit routing rules to disambiguate.
  • In VTGate, we choose to store only the target keyspace and target view name, rather than the target view's definition. The view's definition is then resolved at query time. Alternatively, we can store the target view's definition directly, which would require updating the routing rule when the view's definition changes.
  • We can create an entirely new field in VSchema for view routing rules. This means also creating the equivalent ApplyRoutingRules, etc. commands. This approach would allow us to be more explicit instead of trying to treat views and tables similarly, but requires more effort up front.

Implications

  • The underlying tables that views reference likely have their own routing rules as well. When views are replaced with the target view's definition, the tables will still have their own routing applied as well. In the case of MoveTables, the routing rules will be configured in the same direction, but in the case of manual routing rules, there might be some conflicting routing if users are not careful.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions