Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e10520a

Browse files
committed
Generate unique system ids in cached_initdb()
1 parent d800f48 commit e10520a

File tree

5 files changed

+71
-8
lines changed

5 files changed

+71
-8
lines changed

testgres/cache.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
# coding: utf-8
22

3+
import io
34
import os
45
import shutil
56

67
from six import raise_from
78

89
from .config import testgres_config
910

11+
from .consts import XLOG_CONTROL_FILE
12+
1013
from .exceptions import \
1114
InitNodeException, \
1215
ExecUtilException
1316

1417
from .utils import \
1518
get_bin_path, \
16-
execute_utility
19+
execute_utility, \
20+
generate_system_id
1721

1822

1923
def cached_initdb(data_dir, logfile=None, params=None):
@@ -42,5 +46,18 @@ def call_initdb(initdb_dir, log=None):
4246
try:
4347
# Copy cached initdb to current data dir
4448
shutil.copytree(cached_data_dir, data_dir)
49+
50+
# Assign this node a unique system id if asked to
51+
if testgres_config.cached_initdb_unique:
52+
# XXX: write new unique system id to control file
53+
# Some users might rely upon unique system ids, but
54+
# our initdb caching mechanism breaks this contract.
55+
pg_control = os.path.join(data_dir, XLOG_CONTROL_FILE)
56+
with io.open(pg_control, "r+b") as f:
57+
f.write(generate_system_id()) # overwrite id
58+
59+
# XXX: build new WAL segment with our system id
60+
_params = [get_bin_path("pg_resetwal"), "-D", data_dir, "-f"]
61+
execute_utility(_params, logfile)
4562
except Exception as e:
4663
raise_from(InitNodeException("Failed to spawn a node"), e)

testgres/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class GlobalConfig(object):
1515
Attributes:
1616
cache_initdb: shall we use cached initdb instance?
1717
cached_initdb_dir: shall we create a temp dir for cached initdb?
18+
cached_initdb_unique: shall we assign new node a unique system id?
1819
1920
cache_pg_config: shall we cache pg_config results?
2021
@@ -30,6 +31,7 @@ class GlobalConfig(object):
3031

3132
cache_initdb = True
3233
_cached_initdb_dir = None
34+
cached_initdb_unique = False
3335

3436
cache_pg_config = True
3537

testgres/consts.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
DATA_DIR = "data"
55
LOGS_DIR = "logs"
66

7+
# path to control file
8+
XLOG_CONTROL_FILE = "global/pg_control"
9+
710
# names for config files
811
RECOVERY_CONF_FILE = "recovery.conf"
912
PG_AUTO_CONF_FILE = "postgresql.auto.conf"

testgres/utils.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,27 @@ def generate_app_name():
6868
return 'testgres-{}'.format(str(uuid.uuid4()))
6969

7070

71+
def generate_system_id():
72+
"""
73+
Generate a new 64-bit unique system identifier for node.
74+
"""
75+
76+
import datetime
77+
import struct
78+
79+
date = datetime.datetime.now()
80+
secs = int(date.timestamp())
81+
usecs = date.microsecond
82+
83+
system_id = 0
84+
system_id |= (secs << 32)
85+
system_id |= (usecs << 12)
86+
system_id |= (os.getpid() & 0xFFF)
87+
88+
# pack ULL in native byte order
89+
return struct.pack('=Q', system_id)
90+
91+
7192
def execute_utility(args, logfile=None):
7293
"""
7394
Execute utility (pg_ctl, pg_dump etc).

tests/test_simple.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@
3838
get_pg_config
3939

4040
from testgres import bound_ports
41+
from testgres.utils import pg_version_ge
4142

4243

4344
def util_is_executable(util):
4445
def good_properties(f):
45-
return (
46-
os.path.exists(f) and
47-
os.path.isfile(f) and
48-
os.access(f, os.X_OK)
49-
)
46+
# yapf: disable
47+
return (os.path.exists(f) and
48+
os.path.isfile(f) and
49+
os.access(f, os.X_OK))
5050

5151
# try to resolve it
5252
if good_properties(get_bin_path(util)):
@@ -91,6 +91,27 @@ def test_init_after_cleanup(self):
9191
node.cleanup()
9292
node.init().start().execute('select 1')
9393

94+
@unittest.skipUnless(pg_version_ge('9.6'), 'query works on 9.6+')
95+
def test_init_unique_system_id(self):
96+
with scoped_config(
97+
cache_initdb=True, cached_initdb_unique=True) as config:
98+
99+
self.assertTrue(config.cache_initdb)
100+
self.assertTrue(config.cached_initdb_unique)
101+
102+
# spawn two nodes; ids must be different
103+
with get_new_node().init().start() as node1, \
104+
get_new_node().init().start() as node2:
105+
106+
# this function exists in PostgreSQL 9.6+
107+
query = 'select system_identifier from pg_control_system()'
108+
109+
id1 = node1.execute(query)[0]
110+
id2 = node2.execute(query)[0]
111+
112+
# ids must increase
113+
self.assertGreater(id2, id1)
114+
94115
def test_node_exit(self):
95116
base_dir = None
96117

@@ -486,8 +507,7 @@ def test_logging(self):
486507
master.restart()
487508
self.assertTrue(master._logger.is_alive())
488509

489-
@unittest.skipUnless(
490-
util_is_executable("pgbench"), "pgbench may be missing")
510+
@unittest.skipUnless(util_is_executable('pgbench'), 'might be missing')
491511
def test_pgbench(self):
492512
with get_new_node().init().start() as node:
493513

0 commit comments

Comments
 (0)