# Cached Query (/sdks/ruby/how-to/rpc/query-cached)



## Overview [#overview]

**Query response caching** lets the broker answer repeat requests without re-running your handler — useful when a query is expensive to compute (a database lookup, an aggregation, a downstream call) but the same input is asked for repeatedly in a short window. Only the first request pays the processing cost; every other caller gets the same answer straight from the broker.

Set `cache_key` and `cache_ttl` on the `QueryMessage`. The first query with a given key is a miss: it reaches the handler, and the broker stores the response under that key for the TTL. A subsequent query with the same key is a hit — the broker returns the stored response directly without invoking the handler. `cache_hit` on the response tells you which happened.

**Gotchas:** the cache is keyed by the string you choose, not by the query body — if the underlying data changes mid-TTL, callers can get a stale answer until it expires. Keys are scoped per channel, so the same key on another channel is a separate entry. Caching only helps when requests genuinely repeat with the same key.

## Prerequisites [#prerequisites]

* KubeMQ server running on `localhost:50000`
* Ruby SDK installed (`gem install kubemq`)

## Code [#code]

```ruby title="main.rb"
require 'kubemq'

address = ENV.fetch('KUBEMQ_ADDRESS', 'localhost:50000')
channel = 'ruby-rpc.query-cached'

begin
  client = KubeMQ::CQClient.new(address: address, client_id: 'query-cache-example')
  puts "Connected to #{address}"

  cancel = KubeMQ::CancellationToken.new
  sub = KubeMQ::CQ::QueriesSubscription.new(channel: channel)
  client.subscribe_to_queries(sub, cancellation_token: cancel, on_error: lambda { |e|
    puts "Error: #{e.message}"
  }) do |query|
    puts "Handler processing: #{query.metadata}"
    response = KubeMQ::CQ::QueryResponseMessage.new(
      request_id: query.id,
      reply_channel: query.reply_channel,
      executed: true,
      body: 'computed-result',
      metadata: 'cached-response'
    )
    client.send_response(response)
  end
  sleep 1

  msg = KubeMQ::CQ::QueryMessage.new(
    channel: channel,
    timeout: 10_000,
    metadata: 'get-config',
    body: 'config-key',
    cache_key: 'config:main',
    cache_ttl: 60
  )

  result1 = client.send_query(msg)
  puts "Query 1: executed=#{result1.executed}, cache_hit=#{result1.cache_hit}, body=#{result1.body}"

  result2 = client.send_query(msg)
  puts "Query 2: executed=#{result2.executed}, cache_hit=#{result2.cache_hit}, body=#{result2.body}"
rescue KubeMQ::Error => e
  puts "KubeMQ error: #{e.message}"
ensure
  cancel&.cancel
  client&.close
  puts 'Done'
end
```

## How It Works [#how-it-works]

* `cache_key` and `cache_ttl` on `QueryMessage` enable server-side response caching.
* The first query hits the handler; subsequent queries with the same `cache_key` return the cached response.
* `cache_hit` on the response indicates whether the result was served from cache.
* `cache_ttl` is in seconds — after expiry, the next query re-invokes the handler.
* Review timeouts, channel names, and client IDs before running against shared environments.

## Related [#related]

* [Pattern overview](/learn/rpc/getting-started)
* [Ruby SDK Reference](/sdks/ruby/reference)
* [Send Query](/sdks/ruby/tutorials/query-send)
* [Query Group](/sdks/ruby/how-to/rpc/query-group)
