Skip to content

Commit aee3ddf

Browse files
refactoring: internal_utils.read_line_to_pos__bin is added (#420)
- PostgresNodeLogReader::_create_log_info uses it - impl.FileLineReader now supports a start from any position of file
1 parent c9a562f commit aee3ddf

5 files changed

Lines changed: 172 additions & 59 deletions

File tree

src/impl/file_line_reader.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# coding: utf-8
22

3+
from . import internal_utils
4+
35
import typing
46

57
from testgres.operations.os_ops import OsOperations
@@ -19,15 +21,20 @@ def __init__(
1921
os_ops: OsOperations,
2022
file_name: str,
2123
file_encoding: str = "utf-8",
24+
file_pos: int = 0
2225
):
2326
assert isinstance(os_ops, OsOperations)
2427
assert type(file_encoding) is str
2528
self._os_ops = os_ops
2629
self._file_name = file_name
2730
self._file_encoding = file_encoding
28-
self._file_pos = 0
31+
self._file_pos = file_pos
2932
self._buffer_pos = 0
30-
self._buffer = b''
33+
self._buffer = internal_utils.read_line_to_pos__bin(
34+
os_ops,
35+
file_name,
36+
file_pos,
37+
)
3138
return
3239

3340
# interface ----------------------------------------------------------

src/impl/internal_utils.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
from testgres.operations.os_ops import OsOperations
2+
13
import logging
4+
import typing
25

36

47
def send_log(level: int, msg: str) -> None:
@@ -18,3 +21,72 @@ def send_log_debug(msg: str) -> None:
1821
assert type(msg) is str
1922

2023
return send_log(logging.DEBUG, msg)
24+
25+
26+
def read_line_to_pos__bin(
27+
os_ops: OsOperations,
28+
filename: str,
29+
position: int,
30+
) -> bytes:
31+
assert type(filename) is str
32+
assert type(position) is int
33+
assert len(filename) > 0
34+
assert position >= 0
35+
assert os_ops is not None
36+
assert isinstance(os_ops, OsOperations)
37+
38+
if position == 0:
39+
return b''
40+
41+
assert position > 0
42+
43+
read_position = position
44+
result_blocks: typing.List[bytes] = []
45+
46+
C_BACK_READ_BLOCK_SIZE = 4096
47+
48+
while read_position > 0:
49+
if read_position < C_BACK_READ_BLOCK_SIZE:
50+
block_sz = read_position
51+
else:
52+
block_sz = C_BACK_READ_BLOCK_SIZE
53+
54+
assert block_sz > 0
55+
assert block_sz <= C_BACK_READ_BLOCK_SIZE
56+
57+
read_position -= block_sz
58+
59+
assert read_position < position
60+
assert read_position >= 0
61+
62+
block = os_ops.read_binary(filename, read_position, block_sz)
63+
64+
assert type(block) is bytes
65+
66+
if len(block) != block_sz:
67+
err_msg = "[BUG CHECK] Readed block has bad size ({}). Expected size is ({}). File name {}.".format(
68+
len(block),
69+
block_sz,
70+
filename,
71+
)
72+
raise RuntimeError(err_msg)
73+
74+
assert len(block) == block_sz
75+
76+
x = block.rfind(b"\n", 0, block_sz)
77+
78+
if x == -1:
79+
result_blocks.append(block)
80+
continue
81+
82+
if x == block_sz - 1:
83+
break
84+
85+
block = block[x + 1:]
86+
result_blocks.append(block)
87+
break
88+
89+
result = b''.join(reversed(result_blocks))
90+
assert type(result) is bytes
91+
assert len(result) <= (position - read_position)
92+
return result

src/node.py

Lines changed: 6 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2507,7 +2507,7 @@ def read(self) -> typing.List[LogDataBlock]:
25072507

25082508
assert type(self._logs) is dict
25092509

2510-
result = list()
2510+
result: typing.List[__class__.LogDataBlock] = []
25112511

25122512
for file_name, cur_log_info in cur_logs.items():
25132513
assert type(file_name) is str
@@ -2624,55 +2624,11 @@ def _create_log_info(
26242624
tail=b'',
26252625
)
26262626

2627-
read_position = file_size
2628-
tail_blocks: typing.List[bytes] = []
2629-
2630-
C_BACK_READ_BLOCK_SIZE = 4096
2631-
2632-
while read_position > 0:
2633-
if read_position < C_BACK_READ_BLOCK_SIZE:
2634-
read_offset = 0
2635-
else:
2636-
read_offset = read_position - C_BACK_READ_BLOCK_SIZE
2637-
2638-
assert read_offset >= 0
2639-
assert read_offset < file_size
2640-
assert read_offset < read_position
2641-
2642-
block_sz = read_position - read_offset
2643-
2644-
assert block_sz > 0
2645-
2646-
# read from read_offset to file end
2647-
block = os_ops.read_binary(filename, read_offset, block_sz)
2648-
2649-
assert type(block) is bytes
2650-
2651-
if len(block) != block_sz:
2652-
err_msg = "[BUG CHECK] Readed block has bad size ({}). Expected size is ({}). File name {}.".format(
2653-
len(block),
2654-
block_sz,
2655-
filename,
2656-
)
2657-
raise RuntimeError(err_msg)
2658-
2659-
assert len(block) == block_sz
2660-
2661-
x = block.rfind(b"\n", 0, block_sz)
2662-
2663-
if x == -1:
2664-
tail_blocks.append(block)
2665-
read_position = read_offset
2666-
continue
2667-
2668-
if x == block_sz - 1:
2669-
break
2670-
2671-
block = block[x + 1:]
2672-
tail_blocks.append(block)
2673-
break
2674-
2675-
tail = b''.join(reversed(tail_blocks))
2627+
tail = internal_utils.read_line_to_pos__bin(
2628+
os_ops,
2629+
filename,
2630+
file_size,
2631+
)
26762632

26772633
return __class__.LogInfo(
26782634
position=file_size,

tests/units/impl/test_file_line_reader.py

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class tagStep:
3838
write_data: bytes
3939
read_lines: typing.List[typing.Optional[str]]
4040

41+
# --------------------------------------------------------------------
4142
sm_Steps001: typing.List[tagStep] = [
4243
tagStep(
4344
b"",
@@ -91,14 +92,80 @@ class tagStep:
9192

9293
]
9394

95+
# -------------------------------------------------------------------
96+
def test_001__from_beginnig(
97+
self,
98+
os_ops_descr: OsOpsDescr,
99+
):
100+
assert type(os_ops_descr) is OsOpsDescr
101+
assert isinstance(os_ops_descr.os_ops, OsOperations)
102+
103+
__class__.helper__player(
104+
os_ops_descr,
105+
__class__.sm_Steps001,
106+
0,
107+
)
108+
return
109+
94110
# --------------------------------------------------------------------
95-
def test_001__common(
111+
sm_Steps002: typing.List[tagStep] = [
112+
tagStep(
113+
b"abc\ndefg\n",
114+
["abc\n", "defg\n", None]
115+
),
116+
]
117+
118+
# -------------------------------------------------------------------
119+
def test_002__from_1(
96120
self,
97121
os_ops_descr: OsOpsDescr,
98122
):
99123
assert type(os_ops_descr) is OsOpsDescr
100124
assert isinstance(os_ops_descr.os_ops, OsOperations)
101125

126+
__class__.helper__player(
127+
os_ops_descr,
128+
__class__.sm_Steps002,
129+
1,
130+
)
131+
return
132+
133+
# --------------------------------------------------------------------
134+
sm_Steps003: typing.List[tagStep] = [
135+
tagStep(
136+
b"abc\ndefg\n",
137+
["defg\n", None]
138+
),
139+
]
140+
141+
# -------------------------------------------------------------------
142+
def test_003__from_second_line(
143+
self,
144+
os_ops_descr: OsOpsDescr,
145+
):
146+
assert type(os_ops_descr) is OsOpsDescr
147+
assert isinstance(os_ops_descr.os_ops, OsOperations)
148+
149+
__class__.helper__player(
150+
os_ops_descr,
151+
__class__.sm_Steps003,
152+
4,
153+
)
154+
return
155+
156+
# --------------------------------------------------------------------
157+
@staticmethod
158+
def helper__player(
159+
os_ops_descr: OsOpsDescr,
160+
steps: typing.List[tagStep],
161+
initial_pos: int,
162+
):
163+
assert type(os_ops_descr) is OsOpsDescr
164+
assert isinstance(os_ops_descr.os_ops, OsOperations)
165+
assert type(steps) is list
166+
assert type(initial_pos) is int
167+
assert initial_pos >= 0
168+
102169
os_ops = os_ops_descr.os_ops
103170
assert isinstance(os_ops_descr.os_ops, OsOperations)
104171

@@ -110,23 +177,27 @@ def test_001__common(
110177
assert os_ops.path_exists(filename)
111178
assert os_ops.get_file_size(filename) == 0
112179

113-
file_line_reader = FileLineReader(
114-
os_ops,
115-
filename,
116-
file_encoding="utf-8",
117-
)
180+
file_line_reader: typing.Optional[FileLineReader] = None
118181

119182
# -----------------------
120183
nStep = 0
121184

122-
for step in __class__.sm_Steps001:
185+
for step in steps:
123186
nStep += 1
124187

125188
logging.info("-------------------- step: {}".format(nStep))
126189

127190
logging.info("write: {}".format(step.write_data))
128191
os_ops.write(filename, step.write_data, binary=True)
129192

193+
if file_line_reader is None:
194+
file_line_reader = FileLineReader(
195+
os_ops,
196+
filename,
197+
file_encoding="utf-8",
198+
file_pos=initial_pos,
199+
)
200+
130201
nRead = 0
131202
for expected_line in step.read_lines:
132203
nRead += 1

tests/units/node/PostgresNodeLogReader/test_setM001__helper_create_log_info.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ def test_001__common(self, os_ops_descr: OsOpsDescr):
100100
assert log_info.tail == C_DATA5
101101
assert log_info.position == 5 + len(C_DATA5)
102102

103+
# Scenario 7: The log file has one line with the normal end
104+
os_ops.write(filename, b"\n", binary=True, truncate=True)
105+
106+
log_info = PostgresNodeLogReader._create_log_info(os_ops, filename, find_line_start=True)
107+
assert log_info.tail == b""
108+
assert log_info.position == 1
109+
103110
# Cleanup
104111
os_ops.remove_file(filename)
105112
return

0 commit comments

Comments
 (0)