Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,21 @@ set(source_files
src/binsrv/gtids/uuid.hpp
src/binsrv/gtids/uuid.cpp

# models files
src/binsrv/models/binlog_file_record_fwd.hpp
src/binsrv/models/binlog_file_record.hpp

src/binsrv/models/error_response_fwd.hpp
src/binsrv/models/error_response.hpp
src/binsrv/models/error_response.cpp

src/binsrv/models/response_status_type_fwd.hpp
src/binsrv/models/response_status_type.hpp

src/binsrv/models/search_by_timestamp_response_fwd.hpp
src/binsrv/models/search_by_timestamp_response.hpp
src/binsrv/models/search_by_timestamp_response.cpp

# binlog files
src/binsrv/basic_logger_fwd.hpp
src/binsrv/basic_logger.hpp
Expand All @@ -231,6 +246,14 @@ set(source_files
src/binsrv/cout_logger.hpp
src/binsrv/cout_logger.cpp

src/binsrv/ctime_timestamp_fwd.hpp
src/binsrv/ctime_timestamp.hpp
src/binsrv/ctime_timestamp.cpp

src/binsrv/ctime_timestamp_range_fwd.hpp
src/binsrv/ctime_timestamp_range.hpp
src/binsrv/ctime_timestamp_range.cpp

src/binsrv/exception_handling_helpers.hpp
src/binsrv/exception_handling_helpers.cpp

Expand Down
51 changes: 47 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,17 +146,18 @@ The result binary can be found under the following path `ws/percona-binlog-serve

Please run
```bash
./binlog_server <operation_mode> [ <json_config_file> ]
./binlog_server <operation_mode> [ <json_config_file> [ <subcommand_parameter> ] ]
```
where
`<operation_mode>` can be either `version`, `fetch`, or `pull`
and
`<json_config_file>` is an optional parameter (required only when `<operation_mode>` is not `version`) that represents a path to a JSON configuration file (described below).
`<operation_mode>` can be either `version`, `search_by_timestamp`, `fetch`, or `pull`,
`<json_config_file>` is an optional parameter (required when `<operation_mode>` is not `version`) that represents a path to a JSON configuration file (described below),
and `<subcommand_parameter>` is an optional parameter (required only when `<operation_mode>` is `search_by_timestamp`), that represents a valid timestamp in ISO format (e.g. `2026-02-10T14:30:00`)

### Operation modes

Percona Binary Log Server utility can operate in three modes:
- 'version'
- 'search_by_timestamp'
- 'fetch'
- 'pull'

Expand All @@ -172,6 +173,48 @@ may print
0.1.0
```

#### 'search_by_timestamp' operation mode

In this mode the utility requires one additional command line parameter `<iso_timestamp>` and will print to the standard output the list of binlog files stored in the Binary Log Server data directory that have at least one event whose timestamp is less or equal to the provided `<iso_timestamp>`.
Along with the file name the output will also return its current size in bytes, timestamps and URI.
For instance,
```bash
./binlog_server search_by_timestamp config.json 2026-02-10T14:30:00
```
may print
```json
{
"status": "success",
"result": [
{
"name": "binlog.000001",
"size": 134217728,
"uri": "s3://binsrv-bucket/storage/binlog.000001",
"min_timestamp":"2026-02-09T17:22:01",
"max_timestamp":"2026-02-09T17:22:08"
},
{
"name": "binlog.000002",
"size": 134217728,
"uri": "s3://binsrv-bucket/storage/binlog.000002",
"min_timestamp":"2026-02-09T17:22:08",
"max_timestamp":"2026-02-09T17:22:09"
}
]
}
```
If an error occurs,
```json
{
"status": "error",
"message": "<reason>"
}
```
The `<reason>` may be one of the following (but not limited to):
- `Invalid timestamp format`
- `Binlog storage is empty`
- `Timestamp is too old`

#### 'fetch' operation mode

In this mode the utility tries to connect to a remote MySQL server, switch connection to replication mode and read events from all available binary logs already stored on the server. After reading the very last event, the utility gracefully disconnects and exits.
Expand Down
62 changes: 62 additions & 0 deletions mtr/binlog_streaming/r/search_by_timestamp.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
*** Resetting replication at the very beginning of the test.

*** Generating a configuration file in JSON format for the Binlog
*** Server utility.

*** Determining binlog file directory from the server.

*** Creating a temporary directory <BINSRV_STORAGE_PATH> for storing
*** binlog files downloaded via the Binlog Server utility.

*** 1. Executing the Binlog Server utility in the 'search_by_timestamp'
*** mode with an invalid timestamp
include/read_file_to_var.inc

*** 2. Executing the Binlog Server utility in the 'search_by_timestamp'
*** mode with the beginning of Epoch (Jan 1, 1970, 00:00:00) on an
*** empty storage
include/read_file_to_var.inc

*** Creating a simple table.
CREATE TABLE t1(id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id)) ENGINE=InnoDB;

*** Filling the table with some data.
INSERT INTO t1 VALUES();

*** Flushing the first binary log and switching to the second one.
FLUSH BINARY LOGS;

*** Filling the table with more data.
INSERT INTO t1 VALUES();

*** Executing the Binlog Server utility and fetching all events.

*** 3. Executing the Binlog Server utility in the 'search_by_timestamp'
*** mode with the beginning of Epoch (Jan 1, 1970, 00:00:00) on an
*** non-empty storage
include/read_file_to_var.inc

*** 4. Executing the Binlog Server utility in the 'search_by_timestamp'
*** mode with the <before_activity_timestamp>
include/read_file_to_var.inc

*** 5. Executing the Binlog Server utility in the 'search_by_timestamp'
*** mode with the <inside_activity_timestamp> and expecting one binlog
*** file to be returned
include/read_file_to_var.inc

*** 6. Executing the Binlog Server utility in the 'search_by_timestamp'
*** mode with the <after_activity_timestamp> and expecting two binlog
*** files to be returned
include/read_file_to_var.inc

*** Removing the search result file.

*** Dropping the table.
DROP TABLE t1;

*** Removing the Binlog Server utility storage directory.

*** Removing the Binlog Server utility log file.

*** Removing the Binlog Server utility configuration file.
5 changes: 5 additions & 0 deletions mtr/binlog_streaming/t/search_by_timestamp.combinations
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[position]

[gtid]
gtid-mode=on
enforce-gtid-consistency
120 changes: 120 additions & 0 deletions mtr/binlog_streaming/t/search_by_timestamp.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
--source ../include/have_binsrv.inc

--source ../include/v80_v84_compatibility_defines.inc

# in case of --repeat=N, we need to start from a fresh binary log to make
# this test deterministic
--echo *** Resetting replication at the very beginning of the test.
--disable_query_log
eval $stmt_reset_binary_logs_and_gtids;
--enable_query_log

# identifying backend storage type ('file' or 's3')
--source ../include/identify_storage_backend.inc

# creating data directory, configuration file, etc.
--let $binsrv_connect_timeout = 20
--let $binsrv_read_timeout = 60
--let $binsrv_idle_time = 10
--let $binsrv_verify_checksum = TRUE
--let $binsrv_replication_mode = `SELECT IF(@@global.gtid_mode, 'gtid', 'position')`
--let $binsrv_checkpoint_size = 1
--source ../include/set_up_binsrv_environment.inc

--let $timestamp_query = SELECT DATE_FORMAT(CONVERT_TZ(NOW(), @@session.time_zone, '+00:00'),'%Y-%m-%dT%H:%i:%s')

--let $read_from_file = $MYSQL_TMP_DIR/search_result.json

--echo
--echo *** 1. Executing the Binlog Server utility in the 'search_by_timestamp'
--echo *** mode with an invalid timestamp
--error 1
--exec $BINSRV search_by_timestamp $binsrv_config_file_path 20000101T000000 > $read_from_file
--source include/read_file_to_var.inc
--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'error'`)
--assert(`SELECT JSON_EXTRACT('$result', '$.message') = 'Invalid timestamp format'`)

--echo
--echo *** 2. Executing the Binlog Server utility in the 'search_by_timestamp'
--echo *** mode with the beginning of Epoch (Jan 1, 1970, 00:00:00) on an
--echo *** empty storage
--error 1
--exec $BINSRV search_by_timestamp $binsrv_config_file_path 1970-01-01T00:00:00 > $read_from_file
--source include/read_file_to_var.inc
--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'error'`)
--assert(`SELECT JSON_EXTRACT('$result', '$.message') = 'Binlog storage is empty'`)

--let $before_activity_timestamp = 2000-01-01T00:00:00

--echo
--echo *** Creating a simple table.
CREATE TABLE t1(id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id)) ENGINE=InnoDB;

--echo
--echo *** Filling the table with some data.
INSERT INTO t1 VALUES();

--let $inside_activity_timestamp = `$timestamp_query`
--sleep 2

--echo
--echo *** Flushing the first binary log and switching to the second one.
FLUSH BINARY LOGS;

--echo
--echo *** Filling the table with more data.
INSERT INTO t1 VALUES();

--let $after_activity_timestamp = `$timestamp_query`

--echo
--echo *** Executing the Binlog Server utility and fetching all events.
--exec $BINSRV fetch $binsrv_config_file_path > /dev/null

--echo
--echo *** 3. Executing the Binlog Server utility in the 'search_by_timestamp'
--echo *** mode with the beginning of Epoch (Jan 1, 1970, 00:00:00) on an
--echo *** non-empty storage
--error 1
--exec $BINSRV search_by_timestamp $binsrv_config_file_path 1970-01-01T00:00:00 > $read_from_file
--source include/read_file_to_var.inc
--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'error'`)
--assert(`SELECT JSON_EXTRACT('$result', '$.message') = 'Timestamp is too old'`)

--echo
--echo *** 4. Executing the Binlog Server utility in the 'search_by_timestamp'
--echo *** mode with the <before_activity_timestamp>
--error 1
--exec $BINSRV search_by_timestamp $binsrv_config_file_path $before_activity_timestamp > $read_from_file
--source include/read_file_to_var.inc
--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'error'`)
--assert(`SELECT JSON_EXTRACT('$result', '$.message') = 'Timestamp is too old'`)

--echo
--echo *** 5. Executing the Binlog Server utility in the 'search_by_timestamp'
--echo *** mode with the <inside_activity_timestamp> and expecting one binlog
--echo *** file to be returned
--exec $BINSRV search_by_timestamp $binsrv_config_file_path $inside_activity_timestamp > $read_from_file
--source include/read_file_to_var.inc
--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`)
--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) = 1`)

--echo
--echo *** 6. Executing the Binlog Server utility in the 'search_by_timestamp'
--echo *** mode with the <after_activity_timestamp> and expecting two binlog
--echo *** files to be returned
--exec $BINSRV search_by_timestamp $binsrv_config_file_path $after_activity_timestamp > $read_from_file
--source include/read_file_to_var.inc
--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`)
--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) = 2`)

--echo
--echo *** Removing the search result file.
--remove_file $read_from_file

--echo
--echo *** Dropping the table.
DROP TABLE t1;

# cleaning up
--source ../include/tear_down_binsrv_environment.inc
Loading