diff --git a/agent/http.go b/agent/http.go index f6a8b448e4..ad2bbe9779 100644 --- a/agent/http.go +++ b/agent/http.go @@ -723,6 +723,13 @@ func setMeta(resp http.ResponseWriter, m structs.QueryMetaCompat) { setLastContact(resp, m.GetLastContact()) setKnownLeader(resp, m.GetKnownLeader()) setConsistency(resp, m.GetConsistencyLevel()) + setQueryBackend(resp, m.GetBackend()) +} + +func setQueryBackend(resp http.ResponseWriter, backend structs.QueryBackend) { + if b := backend.String(); b != "" { + resp.Header().Set("X-Consul-Query-Backend", b) + } } // setCacheMeta sets http response headers to indicate cache status. diff --git a/agent/structs/protobuf_compat.go b/agent/structs/protobuf_compat.go index 5322288264..667443c9e9 100644 --- a/agent/structs/protobuf_compat.go +++ b/agent/structs/protobuf_compat.go @@ -44,6 +44,7 @@ type QueryMetaCompat interface { SetIndex(uint64) GetConsistencyLevel() string SetConsistencyLevel(string) + GetBackend() QueryBackend } // GetToken helps implement the QueryOptionsCompat interface @@ -269,3 +270,7 @@ func (q *QueryMeta) SetIndex(index uint64) { func (q *QueryMeta) SetConsistencyLevel(consistencyLevel string) { q.ConsistencyLevel = consistencyLevel } + +func (q *QueryMeta) GetBackend() QueryBackend { + return q.Backend +} diff --git a/agent/structs/structs.go b/agent/structs/structs.go index 93c816e4b6..a0d3e10338 100644 --- a/agent/structs/structs.go +++ b/agent/structs/structs.go @@ -339,6 +339,24 @@ func (w WriteRequest) HasTimedOut(start time.Time, rpcHoldTimeout, maxQueryTime, return time.Since(start) > rpcHoldTimeout } +type QueryBackend int + +const ( + QueryBackendBlocking QueryBackend = iota + QueryBackendStreaming +) + +func (q QueryBackend) String() string { + switch q { + case QueryBackendBlocking: + return "blocking-query" + case QueryBackendStreaming: + return "streaming" + default: + return "" + } +} + // QueryMeta allows a query response to include potentially // useful metadata about a query type QueryMeta struct { @@ -363,6 +381,9 @@ type QueryMeta struct { // When NotModified is true, the response will not contain the result of // the query. NotModified bool + + // Backend used to handle this query, either blocking-query or streaming. + Backend QueryBackend } // RegisterRequest is used for the Catalog.Register endpoint diff --git a/proto/pbcommon/common.go b/proto/pbcommon/common.go index e6e981ccfa..eb396ae58e 100644 --- a/proto/pbcommon/common.go +++ b/proto/pbcommon/common.go @@ -2,6 +2,8 @@ package pbcommon import ( "time" + + "github.com/hashicorp/consul/agent/structs" ) // IsRead is always true for QueryOption @@ -97,6 +99,10 @@ func (q *QueryMeta) SetConsistencyLevel(consistencyLevel string) { q.ConsistencyLevel = consistencyLevel } +func (q *QueryMeta) GetBackend() structs.QueryBackend { + return structs.QueryBackend(0) +} + // WriteRequest only applies to writes, always false func (w WriteRequest) IsRead() bool { return false diff --git a/website/content/api-docs/features/blocking.mdx b/website/content/api-docs/features/blocking.mdx index 2b7a9ae860..4087c64d55 100644 --- a/website/content/api-docs/features/blocking.mdx +++ b/website/content/api-docs/features/blocking.mdx @@ -99,6 +99,9 @@ While streaming is a significant optimization over long polling, it will not pop `X-Consul-LastContact` or `X-Consul-KnownLeader` response headers, because the required data is not available to the client. +When the streaming backend is used, API responses will include the `X-Consul-Query-Backend` +header with a value of `streaming`. + ## Hash-based Blocking Queries