Query Methods

TLDR

  • Decorate functions with @query
  • Read-only
  • Executed on a single node
  • No consensus
  • Latency on the order of ~100 milliseconds
  • 5 billion Wasm instruction limit
  • 4 GiB heap limit
  • ~32k queries per second per canister

The most basic way to expose your canister's functionality publicly is through a query method. Here's an example of a simple query method:

from kybra import query


@query
def get_string() -> str:
    return "This is a query method!"

get_string can be called from the outside world through the IC's HTTP API. You'll usually invoke this API from the dfx command line, dfx web UI, or an agent.

From the dfx command line you can call it like this:

dfx canister call my_canister get_string

Query methods are read-only. They do not persist any state changes. Take a look at the following example:

from kybra import query, void

db = {}


@query
def set(key: str, value: str) -> void:
    db[key] = value

Calling set will perform the operation of setting the key item on the db dictionary to value, but after the call finishes that change will be discarded.

This is because query methods are executed on a single node machine and do not go through consensus. This results in lower latencies, perhaps on the order of 100 milliseconds.

There is a limit to how much computation can be done in a single call to a query method. The current query call limit is 5 billion Wasm instructions. Here's an example of a query method that runs the risk of reaching the limit:

from kybra import nat32, query


@query
def pyramid(levels: nat32) -> str:
    levels_array = [0 for _ in range(levels)]
    asterisk_array = [
        ["*" for _ in range(i + 1)] + ["\n"] for i in range(len(levels_array))
    ]
    flattened_array = [element for subarray in asterisk_array for element in subarray]

    return "".join(flattened_array)

From the dfx command line you can call pyramid like this:

dfx canister call my_canister pyramid '(600)'

With an argument of 600, pyramid will fail with an error ...exceeded the instruction limit for single message execution.

Keep in mind that each query method invocation has up to 4 GiB of heap available.

In terms of query scalability, an individual canister likely has an upper bound of ~36k queries per second.