1

I have a client_requests table with approximately 66k records. It has the following structure:

metrics=# \d+ client_requests;

      Column       |              Type              | Collation | Nullable |                   Default                   | Storage  | Stats target | Description 
-------------------+--------------------------------+-----------+----------+---------------------------------------------+----------+--------------+-------------
 id                | bigint                         |           | not null | nextval('client_requests_id_seq'::regclass) | plain    |              | 
 channel_id        | bigint                         |           | not null |                                             | plain    |              | 
 process_id        | uuid                           |           | not null |                                             | plain    |              | 
 route             | character varying(255)         |           |          |                                             | extended |              | 
 route_name        | character varying(255)         |           |          |                                             | extended |              | 
 duration          | double precision               |           | not null |                                             | plain    |              | 
 timestamp         | timestamp(0) without time zone |           | not null |                                             | plain    |              | 
 events            | jsonb                          |           |          |                                             | extended |              | 
 created_at        | timestamp(0) without time zone |           |          |                                             | plain    |              | 
 updated_at        | timestamp(0) without time zone |           |          |                                             | plain    |              | 

Indexes:
    "client_requests_timestamp_idx" btree ("timestamp" DESC)
Foreign-key constraints:
    "client_requests_channel_id_foreign" FOREIGN KEY (channel_id) REFERENCES channels(id)
    "client_requests_process_id_foreign" FOREIGN KEY (process_id) REFERENCES processes(id)
Triggers:
    ts_insert_blocker BEFORE INSERT ON client_requests FOR EACH ROW EXECUTE FUNCTION _timescaledb_internal.insert_blocker()
Child tables: _timescaledb_internal._hyper_1_1_chunk,
              _timescaledb_internal._hyper_1_2_chunk,
              _timescaledb_internal._hyper_1_3_chunk,
              _timescaledb_internal._hyper_1_4_chunk,
              _timescaledb_internal._hyper_1_5_chunk

metrics=# select count(*) from client_requests;
 count 
-------
 66874
(1 row)

However, the following query takes 168ms to run, which is far longer than I would expect (I would expect single digit ms):

select
  time_bucket_gapfill('12 hours', timestamp, '2021-01-13 16:44:55', '2021-02-12 16:44:55') AS ts
 ,coalesce(ROUND(percentile_cont(0.5) within group (order by client_requests.duration)::numeric, 0), 0.00) as typical_response_time
 ,coalesce(ROUND(percentile_cont(0.95) within group (order by client_requests.duration)::numeric, 0), 0.00) as problem_response_time
from "client_requests"
where "timestamp" > NOW() - INTERVAL '30 days'
group by "ts"
order by "ts" asc;

(66 rows)
Time: 168.704 ms

An EXPLAIN ANALYZE query return the following:

Custom Scan (GapFill)  (cost=21746.41..23921.40 rows=38593 width=72) (actual time=152.965..199.968 rows=66 loops=1)
   ->  GroupAggregate  (cost=21746.41..23438.99 rows=38593 width=0) (actual time=152.957..199.864 rows=61 loops=1)
         Group Key: (time_bucket_gapfill('12:00:00'::interval, client_requests."timestamp"))
         ->  Sort  (cost=21746.41..21904.23 rows=63127 width=16) (actual time=152.703..163.472 rows=63101 loops=1)
               Sort Key: (time_bucket_gapfill('12:00:00'::interval, client_requests."timestamp"))
               Sort Method: quicksort  Memory: 4494kB
               ->  Custom Scan (ChunkAppend) on client_requests  (cost=0.00..16713.30 rows=63127 width=16) (actual time=3.251..120.119 rows=63101 loops=1)
                     Chunks excluded during startup: 0
                     ->  Seq Scan on _hyper_1_1_chunk  (cost=0.00..3239.92 rows=8866 width=16) (actual time=3.246..17.159 rows=8868 loops=1)
                           Filter: ("timestamp" > (now() - '30 days'::interval))
                           Rows Removed by Filter: 3813
                     ->  Seq Scan on _hyper_1_2_chunk  (cost=0.00..4520.70 rows=17583 width=16) (actual time=0.020..26.201 rows=17583 loops=1)
                           Filter: ("timestamp" > (now() - '30 days'::interval))
                     ->  Seq Scan on _hyper_1_3_chunk  (cost=0.00..3606.74 rows=16214 width=16) (actual time=0.028..22.497 rows=16214 loops=1)
                           Filter: ("timestamp" > (now() - '30 days'::interval))
                     ->  Seq Scan on _hyper_1_4_chunk  (cost=0.00..3584.34 rows=14191 width=16) (actual time=0.024..21.248 rows=14191 loops=1)
                           Filter: ("timestamp" > (now() - '30 days'::interval))
                     ->  Seq Scan on _hyper_1_5_chunk  (cost=0.00..1603.78 rows=6273 width=16) (actual time=0.021..9.236 rows=6245 loops=1)
                           Filter: ("timestamp" > (now() - '30 days'::interval))
 Planning Time: 2.128 ms
 Execution Time: 201.534 ms
(21 rows)

Time: 205.592 ms

With so few records in the table, its unclear to me why this query is performing so poorly.

2
  • 1
    Why would you use timescaledb on a table with 66k records? A jet airplane is faster than walking, but not if you are only going from your kitchen to your living room. Commented Feb 16, 2021 at 21:23
  • 1
    No need to be snarky @jjanes. This is a test for a much larger rollout. Commented Feb 17, 2021 at 11:21

1 Answer 1

3

You are sequentially scanning 5 partitions to get 63000 rows, gather the data, sort them and execute a TimescaleDB function. 200 milliseconds is not bad for that.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.