------------------------- Operation log structures. ------------------------- The operation log is placed in the pg_control file (file size PG_CONTROL_FILE_SIZE=8192). Operation log size is PG_OPERATION_LOG_FULL_SIZE=4096. Operation log is a ring buffer of 170 elements (24 bytes each) with header (16 bytes). а) Operation log element: typedef struct OperationLogData { uint8 ol_type; /* operation type */ uint8 ol_edition; /* postgres edition */ uint16 ol_count; /* number of operations */ uint32 ol_version; /* postgres version */ pg_time_t ol_timestamp; /* = int64, operation date/time */ XLogRecPtr ol_lsn; /* = uint64, last check point record ptr */ } OperationLogData; Description of fields: * ol_type - event type. The types are contained in an enum: typedef enum { OLT_BOOTSTRAP = 1, /* bootstrap */ OLT_STARTUP, /* server startup */ OLT_RESETWAL, /* pg_resetwal */ OLT_REWIND, /* pg_rewind */ OLT_UPGRADE, /* pg_upgrade */ OLT_PROMOTED, /* promoted */ OLT_NumberOfTypes /* should be last */ } ol_type_enum; * ol_edition - PostgreSQL edition (this field is important for custom PostgreSQL editions). The editions are contained in an enum: typedef enum { ED_PG_ORIGINAL = 0 /* Here can be custom editions */ } PgNumEdition; * ol_count - the number of fired events in case events of this type can be merged (otherwise 1). Maximum is 65536; * ol_version - version formed with using PG_VERSION_NUM (PG_VERSION_NUM*100; for example: 13000800 for v13.8). Two last digits can be used for patch version; * ol_timestamp - date/time of the last fired event in case events of this type can be merged (otherwise - the date/time of the event); * ol_lsn - "Latest checkpoint location" value (ControlFile->checkPoint) which contains in pg_control file at the time of the event. б) Operation log header: typedef struct OperationLogHeader { pg_crc32c ol_crc; /* CRC of operation log ... MUST BE FIRST! */ uint16 ol_first; /* position of first ring buffer element */ uint16 ol_count; /* number of elements in ring buffer */ uint8 ol_pad[8]; /* just for alignment */ } OperationLogHeader; Description of fields: * ol_crc - CRC of operation log (from position (PG_OPERATION_LOG_POS + sizeof(pg_crc32c)) to PG_CONTROL_FILE_SIZE=8192); * ol_first - 0-based position of the first element in the ring buffer; * ol_count - number of elements in the ring buffer. ----------------------- Event processing modes. ----------------------- There are two event processing modes for the operation log: typedef enum { OLM_MERGE = 1, /* insert element only if not exists element * with the same ol_type and ol_version; * otherwise update existing element */ OLM_INSERT, /* insert element into ring buffer 'as is' */ OLM_NumberOfModes /* should be last */ } ol_mode_enum; Description of processing modes: * OLM_MERGE - for a new event need to search in the ring buffer for an event of the same type with the same version number. If an event is found in the buffer, then it is modified: а) the ol_count field value is increased by 1 (until it not reached PG_UINT16_MAX=65535); б) the ol_timestamp field is modified with the current date/time. If event is not found then it added to the buffer (the same as OLM_INSERT mode). * OLM_INSERT - add an event to the ring buffer without searching first. Structure with modes for events: OperationLogTypeDesc OperationLogTypesDescs[] = { {OLT_BOOTSTRAP, OLM_INSERT, "bootstrap"}, {OLT_STARTUP, OLM_MERGE, "startup"}, {OLT_RESETWAL, OLM_MERGE, "pg_resetwal"}, {OLT_REWIND, OLM_MERGE, "pg_rewind"}, {OLT_UPGRADE, OLM_INSERT, "pg_upgrade"}, {OLT_PROMOTED, OLM_INSERT, "promoted"} }; ------------------------ Operation log functions. ------------------------ (см. файл src\include\common\controldata_utils.h) a) Getting operation log by DataDir: OperationLogBuffer *get_controlfile_log(const char *DataDir); b) Writing operation log (log_buffer) to pg_control file by DataDir: void update_controlfile_log(const char *DataDir, OperationLogBuffer *log_buffer, bool do_sync); c) Writing of event with type ol_type to operation log: void put_operation_log_element(const char *DataDir, ol_type_enum ol_type); d) Writing of event with type ol_type to operation log with edition (edition) and version number (version_num): void put_operation_log_element_version(const char *DataDir, ol_type_enum ol_type, PgNumEdition edition, uint32 version_num); e) Function returns the number of events in the operation log (log_buffer): uint16 get_operation_log_count(OperationLogBuffer *log_buffer); f) Function returns the description of the event with number num from the operation log (log_buffer): OperationLogData *get_operation_log_element(OperationLogBuffer *log_buffer, uint16 num); g) Function returns the name of event by type (ol_type). For example, for ol_type=OLT_BOOTSTRAP function returns "bootstrap". const char *get_operation_log_type_name(ol_type_enum ol_type); ------------------------------------------------- Reading the operation log using the SQL function. ------------------------------------------------- For new databases, the system function pg_operation_log() is automatically created. For existing databases, the pg_operation_log() function should be created manually: CREATE OR REPLACE FUNCTION pg_operation_log( OUT event text, OUT edition text, OUT version text, OUT lsn pg_lsn, OUT last timestamptz, OUT count int4) RETURNS SETOF record LANGUAGE INTERNAL STABLE STRICT PARALLEL SAFE AS 'pg_operation_log'; Example of using pg_operation_log() function: >select * from pg_operation_log(); event | edition | version | lsn | last | count ------------+---------+---------+-----------+------------------------+------- startup | vanilla | 10.22.0 | 0/8000028 | 2022-10-27 23:06:27+03 | 1 pg_upgrade | 1c | 15.0.0 | 0/8000028 | 2022-10-27 23:06:27+03 | 1 startup | 1c | 15.0.0 | 0/80001F8 | 2022-10-27 23:06:53+03 | 2 (3 rows) ---------------------- Feature of pg_upgrade. ---------------------- If the operation log is empty, then before writing event "pg_upgrade", the startup event with the previous version is written to the operation log.