Post

Postgres -- Signals

SIGALRM and Timeouts

If you have done some timer things in Linux, you must be familiar with the concept of Interval Timer. If not, please check out the man page. Below example illustrates how to use it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>

static int signal_recv_count;

void sigalrm_handler(int signum)
{
  printf("SIGALRM received, count :%d\n", signal_recv_count);
  signal_recv_count++;
}

int main()
{
  struct itimerval timer={0};
  timer.it_value.tv_sec = 5;
  timer.it_interval.tv_sec = 1;

  printf("start ...\n"); fflush(stdout);
  signal(SIGALRM, &sigalrm_handler);
  setitimer(ITIMER_REAL, &timer, NULL);

  for (int i = 0; i < 10; i++) sleep(10);
}

Output.

1
2
3
4
5
6
7
8
9
10
11
12
$ ./a.out
start ...
SIGALRM received, count :0
SIGALRM received, count :1
SIGALRM received, count :2
SIGALRM received, count :3
SIGALRM received, count :4
SIGALRM received, count :5
SIGALRM received, count :6
SIGALRM received, count :7
SIGALRM received, count :8
SIGALRM received, count :9

When a process exits, Linux cleans up its itimers as well.

This is exactly how Postgres implements all sorts of timeouts.
Interval timer relevant code is inside a single file timeout.c especially function schedule_alarm.

Take statement_timeout as an example. Quote from Postgres official documentation

statement_timeout (integer)

Abort any statement that takes more than the specified amount of time. The timeout is measured from the time a command arrives at the server until it is completed by the server.

For a simple query, most logic is inside function exec_simple_query. You can see that it starts a transaction before parsing the query string, and finishes this transaction after query is finished. Function start_xact_command calls function enable_statement_timeout() which starts the statement_timeout timer. finish_xact_command calls function disable_statement_timeout which stops and removes the statement_timeout timer. Any time used between them are counted!

From the blog post “Postgres – Cliens”, we know that sending result tuples to the client is also a part of this transaction and printtup blocks if the client cannot recv socket packets timely. By the way, the timer measures wall clock. This creates an awkward situation. In languages that support coroutine such as Python, when the Postgres client is implemented using coroutines, the runtime may switch to other bad or cpu-intensive coroutines which blocks the Postgres socket from doing recv. Then the query times out. The query itself is not slow though.

This post is licensed under CC BY 4.0 by the author.