agora inbox for [email protected]help / color / mirror / Atom feed
test_fsync made modular 265+ messages / 2 participants [nested] [flat]
* test_fsync made modular @ 2011-01-15 19:43 Bruce Momjian <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Bruce Momjian @ 2011-01-15 19:43 UTC (permalink / raw) To: pgsql-hackers I have modified test_fsync to use modular C so there is less duplicate code and it can be enhanced easier. Applied patch attached, though the diff is larger than the C file so you might want to just look at the C file in git. -- Bruce Momjian <[email protected]> http://momjian.us EnterpriseDB http://enterprisedb.com + It's impossible for everything to be true. + Attachments: [text/x-diff] /rtmp/modular (23.9K, ../../[email protected]/2-%2Frtmp%2Fmodular) download | inline diff: diff --git a/src/tools/fsync/README b/src/tools/fsync/README index a1c2ae4..5bf6f1f 100644 *** /tmp/pgdiff.20499/6PmS7d_README Sat Jan 15 14:41:11 2011 --- src/tools/fsync/README Sat Jan 15 14:20:47 2011 *************** test_fsync *** 3,9 **** This program tests fsync. The tests are described as part of the program output. ! Usage: test_fsync [-f filename] [loops] test_fsync is intended to give you a reasonable idea of what the fastest fsync_method is on your specific system, as well as supplying diagnostic --- 3,9 ---- This program tests fsync. The tests are described as part of the program output. ! Usage: test_fsync [-f filename] [ops_per_test] test_fsync is intended to give you a reasonable idea of what the fastest fsync_method is on your specific system, as well as supplying diagnostic *************** The output filename defaults to test_fsy *** 16,20 **** test_fsync should be run in the same filesystem as your transaction log directory (pg_xlog). ! Loops default to 2000. Increase this to get more accurate measurements. --- 16,21 ---- test_fsync should be run in the same filesystem as your transaction log directory (pg_xlog). ! Ops-per-test defaults to 2000. Increase this to get more accurate ! measurements. diff --git a/src/tools/fsync/test_fsync.c b/src/tools/fsync/test_fsync.c index ebfd5fd..ce59afa 100644 *** /tmp/pgdiff.20499/aaqVod_test_fsync.c Sat Jan 15 14:41:11 2011 --- src/tools/fsync/test_fsync.c Sat Jan 15 14:36:45 2011 *************** *** 22,27 **** --- 22,28 ---- #include <unistd.h> #include <string.h> + /* * put the temp files in the local directory * unless the user specifies otherwise *************** *** 34,54 **** #define NA_FORMAT LABEL_FORMAT "%18s" ! int loops = 2000; ! void die(char *str); void print_elapse(struct timeval start_t, struct timeval stop_t); int main(int argc, char *argv[]) { ! struct timeval start_t, stop_t; ! int tmpfile, i; ! char *full_buf = (char *) malloc(XLOG_SEG_SIZE), ! *buf, *filename = FSYNC_FILENAME; /* ! * arguments: loops and filename (optional) */ if (argc > 2 && strcmp(argv[1], "-f") == 0) { --- 35,93 ---- #define NA_FORMAT LABEL_FORMAT "%18s" ! int ops_per_test = 2000; ! char full_buf[XLOG_SEG_SIZE], *buf, *filename = FSYNC_FILENAME; ! struct timeval start_t, stop_t; ! ! void handle_args(int argc, char *argv[]); ! void prepare_buf(void); ! void test_open(void); ! void test_non_sync(void); ! void test_sync(int writes_per_op); ! void test_open_syncs(void); ! void test_file_descriptor_sync(void); void print_elapse(struct timeval start_t, struct timeval stop_t); + void die(char *str); + int main(int argc, char *argv[]) { ! handle_args(argc, argv); ! ! prepare_buf(); ! ! test_open(); ! ! test_non_sync(); ! ! /* Test using 1 8k write */ ! test_sync(1); ! ! /* Test using 2 8k writes */ ! test_sync(2); ! ! test_open_syncs(); ! ! test_file_descriptor_sync(); ! ! unlink(filename); ! ! return 0; ! } + void + handle_args(int argc, char *argv[]) + { + if (argc > 1 && strcmp(argv[1], "-h") == 0) + { + fprintf(stderr, "test_fsync [-f filename] [ops-per-test]\n"); + exit(1); + } + /* ! * arguments: ops_per_test and filename (optional) */ if (argc > 2 && strcmp(argv[1], "-f") == 0) { *************** main(int argc, char *argv[]) *** 58,67 **** } if (argc > 1) ! loops = atoi(argv[1]); ! for (i = 0; i < XLOG_SEG_SIZE; i++) ! full_buf[i] = random(); /* * test if we can open the target file --- 97,123 ---- } if (argc > 1) ! ops_per_test = atoi(argv[1]); ! printf("Ops-per-test = %d\n\n", ops_per_test); ! } ! ! void ! prepare_buf(void) ! { ! int ops; ! ! /* write random data into buffer */ ! for (ops = 0; ops < XLOG_SEG_SIZE; ops++) ! full_buf[ops] = random(); ! ! buf = (char *) TYPEALIGN(ALIGNOF_XLOG_BUFFER, full_buf); ! } ! ! void ! test_open(void) ! { ! int tmpfile; /* * test if we can open the target file *************** main(int argc, char *argv[]) *** 70,94 **** die("Cannot open output file."); if (write(tmpfile, full_buf, XLOG_SEG_SIZE) != XLOG_SEG_SIZE) die("write failed"); ! /* ! * fsync now so that dirty buffers don't skew later tests ! */ if (fsync(tmpfile) != 0) die("fsync failed"); - close(tmpfile); ! buf = (char *) TYPEALIGN(ALIGNOF_XLOG_BUFFER, full_buf); ! printf("Loops = %d\n\n", loops); /* * Test a simple write without fsync */ ! printf("Simple write:\n"); printf(LABEL_FORMAT, "8k write"); fflush(stdout); gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) { if ((tmpfile = open(filename, O_RDWR, 0)) == -1) die("Cannot open output file."); --- 126,153 ---- die("Cannot open output file."); if (write(tmpfile, full_buf, XLOG_SEG_SIZE) != XLOG_SEG_SIZE) die("write failed"); ! ! /* fsync now so that dirty buffers don't skew later tests */ if (fsync(tmpfile) != 0) die("fsync failed"); ! close(tmpfile); ! } ! void ! test_non_sync(void) ! { ! int tmpfile, ops; /* * Test a simple write without fsync */ ! printf("Simple non-sync'ed write:\n"); printf(LABEL_FORMAT, "8k write"); fflush(stdout); + gettimeofday(&start_t, NULL); ! for (ops = 0; ops < ops_per_test; ops++) { if ((tmpfile = open(filename, O_RDWR, 0)) == -1) die("Cannot open output file."); *************** main(int argc, char *argv[]) *** 98,122 **** } gettimeofday(&stop_t, NULL); print_elapse(start_t, stop_t); ! /* ! * Test all fsync methods using single 8k writes ! */ ! printf("\nCompare file sync methods using one write:\n"); /* * Test open_datasync if available */ #ifdef OPEN_DATASYNC_FLAG ! printf(LABEL_FORMAT, "open_datasync 8k write"); fflush(stdout); if ((tmpfile = open(filename, O_RDWR | O_DSYNC, 0)) == -1) die("Cannot open output file."); gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) { ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); if (lseek(tmpfile, 0, SEEK_SET) == -1) die("seek failed"); } --- 157,193 ---- } gettimeofday(&stop_t, NULL); print_elapse(start_t, stop_t); + } ! void ! test_sync(int writes_per_op) ! { ! int tmpfile, ops, writes; ! ! if (writes_per_op == 1) ! printf("\nCompare file sync methods using one write:\n"); ! else ! printf("\nCompare file sync methods using two writes:\n"); ! printf("(in wal_sync_method preference order)\n"); /* * Test open_datasync if available */ #ifdef OPEN_DATASYNC_FLAG ! if (writes_per_op == 1) ! printf(LABEL_FORMAT, "open_datasync 8k write"); ! else ! printf(LABEL_FORMAT, "2 open_datasync 8k writes"); fflush(stdout); + if ((tmpfile = open(filename, O_RDWR | O_DSYNC, 0)) == -1) die("Cannot open output file."); gettimeofday(&start_t, NULL); ! for (ops = 0; ops < ops_per_test; ops++) { ! for (writes = 0; writes < writes_per_op; writes++) ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); if (lseek(tmpfile, 0, SEEK_SET) == -1) die("seek failed"); } *************** main(int argc, char *argv[]) *** 128,144 **** * If O_DIRECT is enabled, test that with open_datasync */ #if PG_O_DIRECT != 0 - fflush(stdout); if ((tmpfile = open(filename, O_RDWR | O_DSYNC | PG_O_DIRECT, 0)) == -1) printf(NA_FORMAT, "o_direct", "n/a on this filesystem\n"); else { ! printf(LABEL_FORMAT, "open_datasync 8k direct I/O write"); gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) { ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); if (lseek(tmpfile, 0, SEEK_SET) == -1) die("seek failed"); } --- 199,220 ---- * If O_DIRECT is enabled, test that with open_datasync */ #if PG_O_DIRECT != 0 if ((tmpfile = open(filename, O_RDWR | O_DSYNC | PG_O_DIRECT, 0)) == -1) printf(NA_FORMAT, "o_direct", "n/a on this filesystem\n"); else { ! if (writes_per_op == 1) ! printf(LABEL_FORMAT, "open_datasync 8k direct I/O write"); ! else ! printf(LABEL_FORMAT, "2 open_datasync 8k direct I/O writes"); ! fflush(stdout); ! gettimeofday(&start_t, NULL); ! for (ops = 0; ops < ops_per_test; ops++) { ! for (writes = 0; writes < writes_per_op; writes++) ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); if (lseek(tmpfile, 0, SEEK_SET) == -1) die("seek failed"); } *************** main(int argc, char *argv[]) *** 155,222 **** #endif /* - * Test open_sync if available - */ - #ifdef OPEN_SYNC_FLAG - printf(LABEL_FORMAT, "open_sync 8k write"); - fflush(stdout); - if ((tmpfile = open(filename, O_RDWR | OPEN_SYNC_FLAG, 0)) == -1) - die("Cannot open output file."); - gettimeofday(&start_t, NULL); - for (i = 0; i < loops; i++) - { - if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) - die("write failed"); - if (lseek(tmpfile, 0, SEEK_SET) == -1) - die("seek failed"); - } - gettimeofday(&stop_t, NULL); - close(tmpfile); - print_elapse(start_t, stop_t); - - /* - * If O_DIRECT is enabled, test that with open_sync - */ - #if PG_O_DIRECT != 0 - printf(LABEL_FORMAT, "open_sync 8k direct I/O write"); - fflush(stdout); - if ((tmpfile = open(filename, O_RDWR | OPEN_SYNC_FLAG | PG_O_DIRECT, 0)) == -1) - printf(NA_FORMAT, "o_direct", "n/a on this filesystem\n"); - else - { - gettimeofday(&start_t, NULL); - for (i = 0; i < loops; i++) - { - if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) - die("write failed"); - if (lseek(tmpfile, 0, SEEK_SET) == -1) - die("seek failed"); - } - gettimeofday(&stop_t, NULL); - close(tmpfile); - print_elapse(start_t, stop_t); - } - #else - printf(NA_FORMAT, "o_direct", "n/a\n"); - #endif - - #else - printf(NA_FORMAT, "open_sync", "n/a\n"); - #endif - - /* * Test fdatasync if available */ #ifdef HAVE_FDATASYNC ! printf(LABEL_FORMAT, "8k write, fdatasync"); fflush(stdout); if ((tmpfile = open(filename, O_RDWR, 0)) == -1) die("Cannot open output file."); gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) { ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); fdatasync(tmpfile); if (lseek(tmpfile, 0, SEEK_SET) == -1) die("seek failed"); --- 231,253 ---- #endif /* * Test fdatasync if available */ #ifdef HAVE_FDATASYNC ! if (writes_per_op == 1) ! printf(LABEL_FORMAT, "8k write, fdatasync"); ! else ! printf(LABEL_FORMAT, "8k write, 8k write, fdatasync"); fflush(stdout); + if ((tmpfile = open(filename, O_RDWR, 0)) == -1) die("Cannot open output file."); gettimeofday(&start_t, NULL); ! for (ops = 0; ops < ops_per_test; ops++) { ! for (writes = 0; writes < writes_per_op; writes++) ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); fdatasync(tmpfile); if (lseek(tmpfile, 0, SEEK_SET) == -1) die("seek failed"); *************** main(int argc, char *argv[]) *** 231,245 **** /* * Test fsync */ ! printf(LABEL_FORMAT, "8k write, fsync"); fflush(stdout); if ((tmpfile = open(filename, O_RDWR, 0)) == -1) die("Cannot open output file."); gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) { ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); if (fsync(tmpfile) != 0) die("fsync failed"); if (lseek(tmpfile, 0, SEEK_SET) == -1) --- 262,281 ---- /* * Test fsync */ ! if (writes_per_op == 1) ! printf(LABEL_FORMAT, "8k write, fsync"); ! else ! printf(LABEL_FORMAT, "8k write, 8k write, fsync"); fflush(stdout); + if ((tmpfile = open(filename, O_RDWR, 0)) == -1) die("Cannot open output file."); gettimeofday(&start_t, NULL); ! for (ops = 0; ops < ops_per_test; ops++) { ! for (writes = 0; writes < writes_per_op; writes++) ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); if (fsync(tmpfile) != 0) die("fsync failed"); if (lseek(tmpfile, 0, SEEK_SET) == -1) *************** main(int argc, char *argv[]) *** 253,267 **** * If fsync_writethrough is available, test as well */ #ifdef HAVE_FSYNC_WRITETHROUGH ! printf(LABEL_FORMAT, "8k write, fsync_writethrough"); fflush(stdout); if ((tmpfile = open(filename, O_RDWR, 0)) == -1) die("Cannot open output file."); gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) { ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); if (fcntl(tmpfile, F_FULLFSYNC ) != 0) die("fsync failed"); if (lseek(tmpfile, 0, SEEK_SET) == -1) --- 289,308 ---- * If fsync_writethrough is available, test as well */ #ifdef HAVE_FSYNC_WRITETHROUGH ! if (writes_per_op == 1) ! printf(LABEL_FORMAT, "8k write, fsync_writethrough"); ! else ! printf(LABEL_FORMAT, "8k write, 8k write, fsync_writethrough"); fflush(stdout); + if ((tmpfile = open(filename, O_RDWR, 0)) == -1) die("Cannot open output file."); gettimeofday(&start_t, NULL); ! for (ops = 0; ops < ops_per_test; ops++) { ! for (writes = 0; writes < writes_per_op; writes++) ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); if (fcntl(tmpfile, F_FULLFSYNC ) != 0) die("fsync failed"); if (lseek(tmpfile, 0, SEEK_SET) == -1) *************** main(int argc, char *argv[]) *** 274,373 **** printf(NA_FORMAT, "fsync_writethrough", "n/a\n"); #endif - /* - * Compare some of the file sync methods with - * two 8k writes to see if timing is different - */ - printf("\nCompare file sync methods using two writes:\n"); - /* ! * Test open_datasync with and without o_direct ! */ ! #ifdef OPEN_DATASYNC_FLAG ! printf(LABEL_FORMAT, "2 open_datasync 8k writes"); ! fflush(stdout); ! if ((tmpfile = open(filename, O_RDWR | O_DSYNC, 0)) == -1) ! die("Cannot open output file."); ! gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) ! { ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); ! if (lseek(tmpfile, 0, SEEK_SET) == -1) ! die("seek failed"); ! } ! gettimeofday(&stop_t, NULL); ! close(tmpfile); ! print_elapse(start_t, stop_t); ! ! #if PG_O_DIRECT != 0 ! printf(LABEL_FORMAT, "2 open_datasync direct I/O 8k writes"); ! fflush(stdout); ! if ((tmpfile = open(filename, O_RDWR | O_DSYNC | PG_O_DIRECT, 0)) == -1) ! die("Cannot open output file."); ! gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) ! { ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); ! if (lseek(tmpfile, 0, SEEK_SET) == -1) ! die("seek failed"); ! } ! gettimeofday(&stop_t, NULL); ! close(tmpfile); ! print_elapse(start_t, stop_t); ! #else ! printf(NA_FORMAT, "o_direct" "n/a\n"); ! #endif ! ! #else ! printf(NA_FORMAT, "open_datasync", "n/a\n"); ! #endif ! ! /* ! * Test open_sync with and without o_direct */ #ifdef OPEN_SYNC_FLAG ! printf(LABEL_FORMAT, "2 open_sync 8k writes"); fflush(stdout); if ((tmpfile = open(filename, O_RDWR | OPEN_SYNC_FLAG, 0)) == -1) die("Cannot open output file."); gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) { ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); if (lseek(tmpfile, 0, SEEK_SET) == -1) die("seek failed"); } gettimeofday(&stop_t, NULL); close(tmpfile); print_elapse(start_t, stop_t); ! #if PG_O_DIRECT != 0 ! printf(LABEL_FORMAT, "2 open_sync direct I/O 8k writes"); fflush(stdout); if ((tmpfile = open(filename, O_RDWR | OPEN_SYNC_FLAG | PG_O_DIRECT, 0)) == -1) ! die("Cannot open output file."); ! gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) { ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); ! if (lseek(tmpfile, 0, SEEK_SET) == -1) ! die("seek failed"); } - gettimeofday(&stop_t, NULL); - close(tmpfile); - print_elapse(start_t, stop_t); #else printf(NA_FORMAT, "o_direct", "n/a\n"); #endif --- 315,372 ---- printf(NA_FORMAT, "fsync_writethrough", "n/a\n"); #endif /* ! * Test open_sync if available */ #ifdef OPEN_SYNC_FLAG ! if (writes_per_op == 1) ! printf(LABEL_FORMAT, "open_sync 8k write"); ! else ! printf(LABEL_FORMAT, "2 open_sync 8k writes"); fflush(stdout); + if ((tmpfile = open(filename, O_RDWR | OPEN_SYNC_FLAG, 0)) == -1) die("Cannot open output file."); gettimeofday(&start_t, NULL); ! for (ops = 0; ops < ops_per_test; ops++) { ! for (writes = 0; writes < writes_per_op; writes++) ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); if (lseek(tmpfile, 0, SEEK_SET) == -1) die("seek failed"); } gettimeofday(&stop_t, NULL); close(tmpfile); print_elapse(start_t, stop_t); ! ! /* ! * If O_DIRECT is enabled, test that with open_sync ! */ #if PG_O_DIRECT != 0 ! if (writes_per_op == 1) ! printf(LABEL_FORMAT, "open_sync 8k direct I/O write"); ! else ! printf(LABEL_FORMAT, "2 open_sync 8k direct I/O writes"); fflush(stdout); + if ((tmpfile = open(filename, O_RDWR | OPEN_SYNC_FLAG | PG_O_DIRECT, 0)) == -1) ! printf(NA_FORMAT, "o_direct", "n/a on this filesystem\n"); ! else { ! gettimeofday(&start_t, NULL); ! for (ops = 0; ops < ops_per_test; ops++) ! { ! for (writes = 0; writes < writes_per_op; writes++) ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); ! if (lseek(tmpfile, 0, SEEK_SET) == -1) ! die("seek failed"); ! } ! gettimeofday(&stop_t, NULL); ! close(tmpfile); ! print_elapse(start_t, stop_t); } #else printf(NA_FORMAT, "o_direct", "n/a\n"); #endif *************** main(int argc, char *argv[]) *** 375,456 **** #else printf(NA_FORMAT, "open_sync", "n/a\n"); #endif ! /* ! * Test fdatasync ! */ ! #ifdef HAVE_FDATASYNC ! printf(LABEL_FORMAT, "8k write, 8k write, fdatasync"); ! fflush(stdout); ! if ((tmpfile = open(filename, O_RDWR, 0)) == -1) ! die("Cannot open output file."); ! gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) ! { ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); ! fdatasync(tmpfile); ! if (lseek(tmpfile, 0, SEEK_SET) == -1) ! die("seek failed"); ! } ! gettimeofday(&stop_t, NULL); ! close(tmpfile); ! print_elapse(start_t, stop_t); ! #else ! printf(NA_FORMAT, "fdatasync", "n/a\n"); ! #endif ! ! /* ! * Test basic fsync ! */ ! printf(LABEL_FORMAT, "8k write, 8k write, fsync"); ! fflush(stdout); ! if ((tmpfile = open(filename, O_RDWR, 0)) == -1) ! die("Cannot open output file."); ! gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) ! { ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); ! if (fsync(tmpfile) != 0) ! die("fsync failed"); ! if (lseek(tmpfile, 0, SEEK_SET) == -1) ! die("seek failed"); ! } ! gettimeofday(&stop_t, NULL); ! close(tmpfile); ! print_elapse(start_t, stop_t); ! ! /* ! * Test fsync_writethrough if available ! */ ! #ifdef HAVE_FSYNC_WRITETHROUGH ! printf(LABEL_FORMAT, "8k write, 8k write, fsync_writethrough"); ! fflush(stdout); ! if ((tmpfile = open(filename, O_RDWR, 0)) == -1) ! die("Cannot open output file."); ! gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) ! { ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); ! if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) ! die("write failed"); ! if (fcntl(tmpfile, F_FULLFSYNC) != 0) ! die("fsync failed"); ! if (lseek(tmpfile, 0, SEEK_SET) == -1) ! die("seek failed"); ! } ! gettimeofday(&stop_t, NULL); ! close(tmpfile); ! print_elapse(start_t, stop_t); ! #else ! printf(NA_FORMAT, "fsync_writethrough", "n/a\n"); ! #endif /* * Compare 1 to 2 writes --- 374,385 ---- #else printf(NA_FORMAT, "open_sync", "n/a\n"); #endif + } ! void ! test_open_syncs(void) ! { ! int tmpfile, ops; /* * Compare 1 to 2 writes *************** main(int argc, char *argv[]) *** 465,474 **** #ifdef OPEN_SYNC_FLAG printf(LABEL_FORMAT, "open_sync 16k write"); fflush(stdout); if ((tmpfile = open(filename, O_RDWR | OPEN_SYNC_FLAG, 0)) == -1) die("Cannot open output file."); gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) { if (write(tmpfile, buf, WRITE_SIZE * 2) != WRITE_SIZE * 2) die("write failed"); --- 394,404 ---- #ifdef OPEN_SYNC_FLAG printf(LABEL_FORMAT, "open_sync 16k write"); fflush(stdout); + if ((tmpfile = open(filename, O_RDWR | OPEN_SYNC_FLAG, 0)) == -1) die("Cannot open output file."); gettimeofday(&start_t, NULL); ! for (ops = 0; ops < ops_per_test; ops++) { if (write(tmpfile, buf, WRITE_SIZE * 2) != WRITE_SIZE * 2) die("write failed"); *************** main(int argc, char *argv[]) *** 481,490 **** printf(LABEL_FORMAT, "2 open_sync 8k writes"); fflush(stdout); if ((tmpfile = open(filename, O_RDWR | OPEN_SYNC_FLAG, 0)) == -1) die("Cannot open output file."); gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) { if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) die("write failed"); --- 411,421 ---- printf(LABEL_FORMAT, "2 open_sync 8k writes"); fflush(stdout); + if ((tmpfile = open(filename, O_RDWR | OPEN_SYNC_FLAG, 0)) == -1) die("Cannot open output file."); gettimeofday(&start_t, NULL); ! for (ops = 0; ops < ops_per_test; ops++) { if (write(tmpfile, buf, WRITE_SIZE) != WRITE_SIZE) die("write failed"); *************** main(int argc, char *argv[]) *** 499,504 **** --- 430,441 ---- #else printf(NA_FORMAT, "open_sync", "n/a\n"); #endif + } + + void + test_file_descriptor_sync(void) + { + int tmpfile, ops; /* * Test whether fsync can sync data written on a different *************** main(int argc, char *argv[]) *** 517,524 **** */ printf(LABEL_FORMAT, "8k write, fsync, close"); fflush(stdout); gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) { if ((tmpfile = open(filename, O_RDWR, 0)) == -1) die("Cannot open output file."); --- 454,462 ---- */ printf(LABEL_FORMAT, "8k write, fsync, close"); fflush(stdout); + gettimeofday(&start_t, NULL); ! for (ops = 0; ops < ops_per_test; ops++) { if ((tmpfile = open(filename, O_RDWR, 0)) == -1) die("Cannot open output file."); *************** main(int argc, char *argv[]) *** 545,552 **** */ printf(LABEL_FORMAT, "8k write, close, fsync"); fflush(stdout); gettimeofday(&start_t, NULL); ! for (i = 0; i < loops; i++) { if ((tmpfile = open(filename, O_RDWR, 0)) == -1) die("Cannot open output file."); --- 483,491 ---- */ printf(LABEL_FORMAT, "8k write, close, fsync"); fflush(stdout); + gettimeofday(&start_t, NULL); ! for (ops = 0; ops < ops_per_test; ops++) { if ((tmpfile = open(filename, O_RDWR, 0)) == -1) die("Cannot open output file."); *************** main(int argc, char *argv[]) *** 563,575 **** gettimeofday(&stop_t, NULL); print_elapse(start_t, stop_t); - /* - * cleanup - */ - free(full_buf); - unlink(filename); - - return 0; } /* --- 502,507 ---- *************** print_elapse(struct timeval start_t, str *** 580,586 **** { double total_time = (stop_t.tv_sec - start_t.tv_sec) + (stop_t.tv_usec - start_t.tv_usec) * 0.000001; ! double per_second = loops / total_time; printf("%9.3f ops/sec\n", per_second); } --- 512,518 ---- { double total_time = (stop_t.tv_sec - start_t.tv_sec) + (stop_t.tv_usec - start_t.tv_usec) * 0.000001; ! double per_second = ops_per_test / total_time; printf("%9.3f ops/sec\n", per_second); } ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
* [PATCH v6] README for 64bit xid @ 2022-01-10 19:20 Pavel Borisov <[email protected]> 0 siblings, 0 replies; 265+ messages in thread From: Pavel Borisov @ 2022-01-10 19:20 UTC (permalink / raw) Authors: - Pavel Borisov <[email protected]> - Maxim Orlov <[email protected]> - Yura Sokolov <[email protected]> <[email protected]> --- src/backend/access/heap/README.XID64 | 128 +++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/backend/access/heap/README.XID64 diff --git a/src/backend/access/heap/README.XID64 b/src/backend/access/heap/README.XID64 new file mode 100644 index 00000000000..457ba9b9ef5 --- /dev/null +++ b/src/backend/access/heap/README.XID64 @@ -0,0 +1,128 @@ +src/backend/access/heap/README.XID64 + +64-bit Transaction ID's (XID) +============================= + +A limited number (N = 2^32) of XID's required to do vacuum freeze to prevent +wraparound every N/2 transactions. This causes performance degradation due +to the need to exclusively lock tables while being vacuumed. In each +wraparound cycle, SLRU buffers are also being cut. + +With 64-bit XID's wraparound is effectively postponed to a very distant +future. Even in highly loaded systems that had 2^32 transactions per day +it will take huge 2^31 days before the first enforced "vacuum to prevent +wraparound"). Buffers cutting and routine vacuum are not enforced, and DBA +can plan them independently at the time with the least system load and least +critical for database performance. Also, it can be done less frequently +(several times a year vs every several days) on systems with transaction rates +similar to those mentioned above. + +On-disk tuple and page format +----------------------------- + +On-disk tuple format remains unchanged. 32-bit t_xmin and t_xmax store the +lower parts of 64-bit XMIN and XMAX values. Each heap page has additional +64-bit pd_xid_base and pd_multi_base which are common for all tuples on a page. +They are placed into a pd_special area - 16 bytes in the end of a heap page. +Actual XMIN/XMAX for a tuple are calculated upon reading a tuple from a page +as follows: + +XMIN = t_xmin + pd_xid_base. (1) +XMAX = t_xmax + pd_xid_base/pd_multi_base. (2) + +"Double XMAX" page format +--------------------------------- + +At first read of a heap page after pg_upgrade from 32-bit XID PostgreSQL +version pd_special area with a size of 16 bytes should be added to a page. +Though a page may not have space for this. Then it can be converted to a +temporary format called "double XMAX". + +All tuples after pg-upgrade would necessarily have xmin = FrozenTransactionId. +So we don't need tuple header t_xmin field and we reuse t_xmin to store higher +32 bits of its XMAX. + +Double XMAX format is only for full pages that don't have 16 bytes for +pd_special. So it neither has a�place for a single tuple. Insert and HOT update +for double XMAX pages is impossible and not supported. We can only read or +delete tuples from it. + +When we are able to prune page double XMAX it will be converted from it to +general 64-bit XID page format with all operations on its tuples supported. + +In-memory tuple format +---------------------- + +In-memory tuple representation consists of two parts: +- HeapTupleHeader from disk page (contains all heap tuple contents, not only +header) +- HeapTuple with additional in-memory fields + +HeapTuple for each tuple in memory stores t_xid_base/t_multi_base - a copies of +page's pd_xid_base/pd_multi_base. With tuple's 32-bit t_xmin and t_xmax from +HeapTupleHeader they are used to calculate actual 64-bit XMIN and XMAX: + +XMIN = t_xmin + t_xid_base. (3) +XMAX = t_xmax + t_xid_base/t_multi_base. (4) + +The downside of this is that we can not use tuple's XMIN and XMAX right away. +We often need to re-read t_xmin and t_xmax - which could actually be pointers +into a page in shared buffers and therefore they could be updated by any other +backend. + +Update/delete with 64-bit XIDs and 32-bit t_xmin/t_xmax +-------------------------------------------------------------- + +When we try to delete/update a tuple, we check that XMAX for a page fits (2). +I.e. that t_xmax will not be over MaxShortTransactionId relative to +pd_xid_base/pd_multi_base of a its page. + +If the current XID doesn't fit a range +(pd_xid_base, pd_xid_base + MaxShortTransactionId) (5): + +- heap_page_prepare_for_xid() will try to increase pd_xid_base/pd_multi_base on +a page and update all t_xmin/t_xmax of the other tuples on the page to +correspond new pd_xid_base/pd_multi_base. + +- If it was impossible, it will try to prune and freeze tuples on a page. + +- If this is unsuccessful it will throw an error. Normally this is very +unlikely but if there is a very old living transaction with an age of around +2^32 this can arise. Basically, this is a behavior similar to one during the +vacuum to prevent wraparound when XID was 32-bit. Dba should take care and +avoid very-long-living transactions with an age close to 2^32. So long-living +transactions often they are most likely defunct. + +Insert with 64-bit XIDs and 32-bit t_xmin/t_xmax +------------------------------------------------ + +On insert we check if current XID fits a range (5). Otherwise: + +- heap_page_prepare_for_xid() will try to increase pd_xid_base for t_xmin will +not be over MaxShortTransactionId. + +- If it is impossible, then it will try to prune and freeze tuples on a page. + +Known issue: if pd_xid_base could not be shifted to accommodate a tuple being +inserted due to a very long-running transaction, we just throw an error. We +neither try to insert a�tuple into another page nor mark the current page as +full. So, in this (unlikely) case we will get regular insert errors on the next +tries to insert to the page 'locked' by this very long-running transaction. + +Upgrade from 32-bit XID versions +-------------------------------- + +pg_upgrade doesn't change pages format itself. It is done lazily after. + +1. At first heap page read, tuples on a page are repacked to free 16 bytes +at the end of a page, possibly freeing space from dead tuples. + +2A. 16 bytes of pd_special is added if there is a place for it + +2B. Page is converted to "Double XMAX" format if there is no place for +pd_special + +3. If a page is in double XMAX format after its first read, and vacuum (or +micro-vacuum at select query) could prune some tuples and free space for +pd_special, prune_page will add pd_special and convert page from double XMAX +to general 64-bit XID page format. -- 2.24.3 (Apple Git-128) --cpok4wp6gsarlzvp-- ^ permalink raw reply [nested|flat] 265+ messages in thread
end of thread, other threads:[~2022-01-10 19:20 UTC | newest] Thread overview: 265+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2011-01-15 19:43 test_fsync made modular Bruce Momjian <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]> 2022-01-10 19:20 [PATCH v6] README for 64bit xid Pavel Borisov <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox