5b0d4db6bc
* Add cache types for catalog/services and health/services and basic test that caching works * Support non-blocking cache types with Cache-Control semantics. * Update API docs to include caching info for every endpoint. * Comment updates per PR feedback. * Add note on caching to the 10,000 foot view on the architecture page to make the new data path more clear. * Document prepared query staleness quirk and force all background requests to AllowStale so we can spread service discovery load across servers.
325 lines
10 KiB
Go
325 lines
10 KiB
Go
package structs
|
|
|
|
import (
|
|
"strconv"
|
|
|
|
"github.com/hashicorp/consul/agent/cache"
|
|
"github.com/hashicorp/consul/types"
|
|
"github.com/mitchellh/hashstructure"
|
|
)
|
|
|
|
// QueryDatacenterOptions sets options about how we fail over if there are no
|
|
// healthy nodes in the local datacenter.
|
|
type QueryDatacenterOptions struct {
|
|
// NearestN is set to the number of remote datacenters to try, based on
|
|
// network coordinates.
|
|
NearestN int
|
|
|
|
// Datacenters is a fixed list of datacenters to try after NearestN. We
|
|
// never try a datacenter multiple times, so those are subtracted from
|
|
// this list before proceeding.
|
|
Datacenters []string
|
|
}
|
|
|
|
// QueryDNSOptions controls settings when query results are served over DNS.
|
|
type QueryDNSOptions struct {
|
|
// TTL is the time to live for the served DNS results.
|
|
TTL string
|
|
}
|
|
|
|
// ServiceQuery is used to query for a set of healthy nodes offering a specific
|
|
// service.
|
|
type ServiceQuery struct {
|
|
// Service is the service to query.
|
|
Service string
|
|
|
|
// Failover controls what we do if there are no healthy nodes in the
|
|
// local datacenter.
|
|
Failover QueryDatacenterOptions
|
|
|
|
// If OnlyPassing is true then we will only include nodes with passing
|
|
// health checks (critical AND warning checks will cause a node to be
|
|
// discarded)
|
|
OnlyPassing bool
|
|
|
|
// IgnoreCheckIDs is an optional list of health check IDs to ignore when
|
|
// considering which nodes are healthy. It is useful as an emergency measure
|
|
// to temporarily override some health check that is producing false negatives
|
|
// for example.
|
|
IgnoreCheckIDs []types.CheckID
|
|
|
|
// Near allows the query to always prefer the node nearest the given
|
|
// node. If the node does not exist, results are returned in their
|
|
// normal randomly-shuffled order. Supplying the magic "_agent" value
|
|
// is supported to sort near the agent which initiated the request.
|
|
Near string
|
|
|
|
// Tags are a set of required and/or disallowed tags. If a tag is in
|
|
// this list it must be present. If the tag is preceded with "!" then
|
|
// it is disallowed.
|
|
Tags []string
|
|
|
|
// NodeMeta is a map of required node metadata fields. If a key/value
|
|
// pair is in this map it must be present on the node in order for the
|
|
// service entry to be returned.
|
|
NodeMeta map[string]string
|
|
|
|
// Connect if true will filter the prepared query results to only
|
|
// include Connect-capable services. These include both native services
|
|
// and proxies for matching services. Note that if a proxy matches,
|
|
// the constraints in the query above (Near, OnlyPassing, etc.) apply
|
|
// to the _proxy_ and not the service being proxied. In practice, proxies
|
|
// should be directly next to their services so this isn't an issue.
|
|
Connect bool
|
|
}
|
|
|
|
const (
|
|
// QueryTemplateTypeNamePrefixMatch uses the Name field of the query as
|
|
// a prefix to select the template.
|
|
QueryTemplateTypeNamePrefixMatch = "name_prefix_match"
|
|
)
|
|
|
|
// QueryTemplateOptions controls settings if this query is a template.
|
|
type QueryTemplateOptions struct {
|
|
// Type, if non-empty, means that this query is a template. This is
|
|
// set to one of the QueryTemplateType* constants above.
|
|
Type string
|
|
|
|
// Regexp is an optional regular expression to use to parse the full
|
|
// name, once the prefix match has selected a template. This can be
|
|
// used to extract parts of the name and choose a service name, set
|
|
// tags, etc.
|
|
Regexp string
|
|
|
|
// RemoveEmptyTags, if true, removes empty tags from matched tag list
|
|
RemoveEmptyTags bool
|
|
}
|
|
|
|
// PreparedQuery defines a complete prepared query, and is the structure we
|
|
// maintain in the state store.
|
|
type PreparedQuery struct {
|
|
// ID is this UUID-based ID for the query, always generated by Consul.
|
|
ID string
|
|
|
|
// Name is an optional friendly name for the query supplied by the
|
|
// user. NOTE - if this feature is used then it will reduce the security
|
|
// of any read ACL associated with this query/service since this name
|
|
// can be used to locate nodes with supplying any ACL.
|
|
Name string
|
|
|
|
// Session is an optional session to tie this query's lifetime to. If
|
|
// this is omitted then the query will not expire.
|
|
Session string
|
|
|
|
// Token is the ACL token used when the query was created, and it is
|
|
// used when a query is subsequently executed. This token, or a token
|
|
// with management privileges, must be used to change the query later.
|
|
Token string
|
|
|
|
// Template is used to configure this query as a template, which will
|
|
// respond to queries based on the Name, and then will be rendered
|
|
// before it is executed.
|
|
Template QueryTemplateOptions
|
|
|
|
// Service defines a service query (leaving things open for other types
|
|
// later).
|
|
Service ServiceQuery
|
|
|
|
// DNS has options that control how the results of this query are
|
|
// served over DNS.
|
|
DNS QueryDNSOptions
|
|
|
|
RaftIndex
|
|
}
|
|
|
|
// GetACLPrefix returns the prefix to look up the prepared_query ACL policy for
|
|
// this query, and whether the prefix applies to this query. You always need to
|
|
// check the ok value before using the prefix.
|
|
func (pq *PreparedQuery) GetACLPrefix() (string, bool) {
|
|
if pq.Name != "" || pq.Template.Type != "" {
|
|
return pq.Name, true
|
|
}
|
|
|
|
return "", false
|
|
}
|
|
|
|
type PreparedQueries []*PreparedQuery
|
|
|
|
type IndexedPreparedQueries struct {
|
|
Queries PreparedQueries
|
|
QueryMeta
|
|
}
|
|
|
|
type PreparedQueryOp string
|
|
|
|
const (
|
|
PreparedQueryCreate PreparedQueryOp = "create"
|
|
PreparedQueryUpdate PreparedQueryOp = "update"
|
|
PreparedQueryDelete PreparedQueryOp = "delete"
|
|
)
|
|
|
|
// QueryRequest is used to create or change prepared queries.
|
|
type PreparedQueryRequest struct {
|
|
// Datacenter is the target this request is intended for.
|
|
Datacenter string
|
|
|
|
// Op is the operation to apply.
|
|
Op PreparedQueryOp
|
|
|
|
// Query is the query itself.
|
|
Query *PreparedQuery
|
|
|
|
// WriteRequest holds the ACL token to go along with this request.
|
|
WriteRequest
|
|
}
|
|
|
|
// RequestDatacenter returns the datacenter for a given request.
|
|
func (q *PreparedQueryRequest) RequestDatacenter() string {
|
|
return q.Datacenter
|
|
}
|
|
|
|
// PreparedQuerySpecificRequest is used to get information about a prepared
|
|
// query.
|
|
type PreparedQuerySpecificRequest struct {
|
|
// Datacenter is the target this request is intended for.
|
|
Datacenter string
|
|
|
|
// QueryID is the ID of a query.
|
|
QueryID string
|
|
|
|
// QueryOptions (unfortunately named here) controls the consistency
|
|
// settings for the query lookup itself, as well as the service lookups.
|
|
QueryOptions
|
|
}
|
|
|
|
// RequestDatacenter returns the datacenter for a given request.
|
|
func (q *PreparedQuerySpecificRequest) RequestDatacenter() string {
|
|
return q.Datacenter
|
|
}
|
|
|
|
// PreparedQueryExecuteRequest is used to execute a prepared query.
|
|
type PreparedQueryExecuteRequest struct {
|
|
// Datacenter is the target this request is intended for.
|
|
Datacenter string
|
|
|
|
// QueryIDOrName is the ID of a query _or_ the name of one, either can
|
|
// be provided.
|
|
QueryIDOrName string
|
|
|
|
// Limit will trim the resulting list down to the given limit.
|
|
Limit int
|
|
|
|
// Connect will force results to be Connect-enabled nodes for the
|
|
// matching services. This is equivalent in semantics exactly to
|
|
// setting "Connect" in the query template itself, but allows callers
|
|
// to use any prepared query in a Connect setting.
|
|
Connect bool
|
|
|
|
// Source is used to sort the results relative to a given node using
|
|
// network coordinates.
|
|
Source QuerySource
|
|
|
|
// Agent is used to carry around a reference to the agent which initiated
|
|
// the execute request. Used to distance-sort relative to the local node.
|
|
Agent QuerySource
|
|
|
|
// QueryOptions (unfortunately named here) controls the consistency
|
|
// settings for the query lookup itself, as well as the service lookups.
|
|
QueryOptions
|
|
}
|
|
|
|
// RequestDatacenter returns the datacenter for a given request.
|
|
func (q *PreparedQueryExecuteRequest) RequestDatacenter() string {
|
|
return q.Datacenter
|
|
}
|
|
|
|
// CacheInfo implements cache.Request allowing requests to be cached on agent.
|
|
func (q *PreparedQueryExecuteRequest) CacheInfo() cache.RequestInfo {
|
|
info := cache.RequestInfo{
|
|
Token: q.Token,
|
|
Datacenter: q.Datacenter,
|
|
MinIndex: q.MinQueryIndex,
|
|
Timeout: q.MaxQueryTime,
|
|
MaxAge: q.MaxAge,
|
|
MustRevalidate: q.MustRevalidate,
|
|
}
|
|
|
|
// To calculate the cache key we hash over all the fields that affect the
|
|
// output other than Datacenter and Token which are dealt with in the cache
|
|
// framework already. Note the order here is important for the outcome - if we
|
|
// ever care about cache-invalidation on updates e.g. because we persist
|
|
// cached results, we need to be careful we maintain the same order of fields
|
|
// here. We could alternatively use `hash:set` struct tag on an anonymous
|
|
// struct to make it more robust if it becomes significant.
|
|
v, err := hashstructure.Hash([]interface{}{
|
|
q.QueryIDOrName,
|
|
q.Limit,
|
|
q.Connect,
|
|
}, nil)
|
|
if err == nil {
|
|
// If there is an error, we don't set the key. A blank key forces
|
|
// no cache for this request so the request is forwarded directly
|
|
// to the server.
|
|
info.Key = strconv.FormatUint(v, 10)
|
|
}
|
|
|
|
return info
|
|
}
|
|
|
|
// PreparedQueryExecuteRemoteRequest is used when running a local query in a
|
|
// remote datacenter.
|
|
type PreparedQueryExecuteRemoteRequest struct {
|
|
// Datacenter is the target this request is intended for.
|
|
Datacenter string
|
|
|
|
// Query is a copy of the query to execute. We have to ship the entire
|
|
// query over since it won't be present in the remote state store.
|
|
Query PreparedQuery
|
|
|
|
// Limit will trim the resulting list down to the given limit.
|
|
Limit int
|
|
|
|
// Connect is the same as ExecuteRequest.
|
|
Connect bool
|
|
|
|
// QueryOptions (unfortunately named here) controls the consistency
|
|
// settings for the the service lookups.
|
|
QueryOptions
|
|
}
|
|
|
|
// RequestDatacenter returns the datacenter for a given request.
|
|
func (q *PreparedQueryExecuteRemoteRequest) RequestDatacenter() string {
|
|
return q.Datacenter
|
|
}
|
|
|
|
// PreparedQueryExecuteResponse has the results of executing a query.
|
|
type PreparedQueryExecuteResponse struct {
|
|
// Service is the service that was queried.
|
|
Service string
|
|
|
|
// Nodes has the nodes that were output by the query.
|
|
Nodes CheckServiceNodes
|
|
|
|
// DNS has the options for serving these results over DNS.
|
|
DNS QueryDNSOptions
|
|
|
|
// Datacenter is the datacenter that these results came from.
|
|
Datacenter string
|
|
|
|
// Failovers is a count of how many times we had to query a remote
|
|
// datacenter.
|
|
Failovers int
|
|
|
|
// QueryMeta has freshness information about the query.
|
|
QueryMeta
|
|
}
|
|
|
|
// PreparedQueryExplainResponse has the results when explaining a query/
|
|
type PreparedQueryExplainResponse struct {
|
|
// Query has the fully-rendered query.
|
|
Query PreparedQuery
|
|
|
|
// QueryMeta has freshness information about the query.
|
|
QueryMeta
|
|
}
|