Datadog questions

Async Query Client

DatadogOnsiteMedium

You are given the class QueryClient below. Its API methods are already implemented and scripted by the test; you only implement run.

  • submit_query(sql) starts a query and returns {"query_id": ...}.
  • get_query_status(query_id) returns the next scripted response: {"status": "RUNNING"} or {"status": "QUEUED"} while the query is in progress; {"status": "SUCCEEDED", "rows": [...], "next_token": ...} with the first page of rows and the token of the next page (next_token is None when there is none); {"status": "FAILED", "error": message}; or {"status": "CANCELED"}.
  • fetch_result_page(query_id, token) returns {"rows": [...], "next_token": ...} for one later page.
  • stats() returns [queries_submitted, status_calls, page_fetches].

The tests build the client with the status responses to return in order — the last one repeats once the script runs out — and a dict of pages keyed by token, call run, then call stats.

class QueryClient:
    def __init__(self, polls: list[dict], pages: dict[str, dict], max_polls: int) -> None:
        self.max_polls = max_polls
        self._polls = polls
        self._pages = pages
        self._submits = 0
        self._status_calls = 0
        self._page_calls = 0

    def submit_query(self, sql: str) -> dict:
        """Starts a query and returns {"query_id": ...}."""
        self._submits += 1
        return {"query_id": f"q{self._submits}"}

    def get_query_status(self, query_id: str) -> dict:
        """The next scripted response; the last one repeats once the script runs out."""
        index = min(self._status_calls, len(self._polls) - 1)
        self._status_calls += 1
        return self._polls[index]

    def fetch_result_page(self, query_id: str, token: str) -> dict:
        """The page stored under `token`: {"rows": [...], "next_token": ... or None}."""
        self._page_calls += 1
        return self._pages[token]

    def stats(self) -> list[int]:
        """[queries submitted, status calls made, page fetches made]."""
        return [self._submits, self._status_calls, self._page_calls]

    def run(self, sql: str) -> list:
        raise NotImplementedError

Part 1: Run a query to completion

Implement run(sql).

run submits the query exactly once with submit_query, then calls get_query_status until the status is SUCCEEDED, FAILED or CANCELED, or until max_polls calls have been made. It stops polling as soon as the status is final.

When the status is SUCCEEDED, run first follows next_token and fetches every later page with fetch_result_page, one page at a time, never refetching the first page. Only then does it decide the return value:

  • the rows of all pages in order, first page first, when the total collected rows are not empty;
  • ["EMPTY"] when the total collected rows across all pages are empty.

Otherwise it returns:

  • ["ERROR: <message>"] using the failure's error text when the status is FAILED.
  • ["ERROR: canceled"] when the status is CANCELED.
  • ["TIMEOUT"] when max_polls status calls were made and the query was still RUNNING or QUEUED.
  1. Example 1
    init
    [[{"status":"RUNNING"},{"rows":[[1,"alice"],[2,"bob"]],"status":"SUCCEEDED","next_token":null}],{},3]
    operations
    [["run",["SELECT id, name FROM users"]],["stats",[]]]
    Output
    [[[1,"alice"],[2,"bob"]],[1,2,0]]

    Why: The first poll returns RUNNING, so run polls again. The second poll returns SUCCEEDED with both rows and no next token, so the rows are returned and no page is fetched.

  2. Example 2
    init
    [[{"rows":[[1]],"status":"SUCCEEDED","next_token":"p2"}],{"p2":{"rows":[[2],[3]],"next_token":"p3"},"p3":{"rows":[[4]],"next_token":null}},5]
    operations
    [["run",["SELECT id, name FROM users"]],["stats",[]]]
    Output
    [[[1],[2],[3],[4]],[1,1,2]]

    Why: The SUCCEEDED response carries the first page [[1]] and token p2. run fetches p2 for [[2], [3]] and p3 for [[4]], whose next token is None, and returns the rows in order. The first page is never fetched, so there are 2 page fetches.

Constraints

  • 1 <= max_polls <= 100
  • 1 <= len(polls)
  • Every rows[i][j] is an integer or a string
  • Every next_token that is reached has a page, and the tokens do not loop
  • run is called once per client
Import your own questions
Paste a question you were asked and get a full problem: statement, tests and a checked solution.
Import a question