2
2
import traceback
3
3
import random
4
4
import time
5
- import aioprocessing as ap
5
+ import queue
6
6
import multiprocessing as mp
7
7
8
8
from pyTON .settings import TonlibSettings
9
9
from pyTON .models import TonlibWorkerMsgType , TonlibClientResult
10
10
from pytonlib import TonlibClient
11
11
from datetime import datetime
12
+ from concurrent .futures import ThreadPoolExecutor
12
13
13
14
from enum import Enum
14
15
from dataclasses import dataclass
@@ -21,14 +22,13 @@ class TonlibWorker(mp.Process):
21
22
def __init__ (self ,
22
23
ls_index : int ,
23
24
tonlib_settings : TonlibSettings ,
24
- input_queue : Optional [ap . AioQueue ]= None ,
25
- output_queue : Optional [ap . AioQueue ]= None ):
25
+ input_queue : Optional [mp . Queue ]= None ,
26
+ output_queue : Optional [mp . Queue ]= None ):
26
27
super (TonlibWorker , self ).__init__ (daemon = True )
27
28
28
- self .input_queue = input_queue or ap .AioQueue ()
29
- self .output_queue = output_queue or ap .AioQueue ()
30
- self .input_queue .cancel_join_thread ()
31
- self .output_queue .cancel_join_thread ()
29
+ self .input_queue = input_queue or mp .Queue ()
30
+ self .output_queue = output_queue or mp .Queue ()
31
+ self .exit_event = mp .Event ()
32
32
33
33
self .ls_index = ls_index
34
34
self .tonlib_settings = tonlib_settings
@@ -39,11 +39,14 @@ def __init__(self,
39
39
self .loop = None
40
40
self .tasks = {}
41
41
self .tonlib = None
42
+ self .threadpool_executor = None
42
43
43
44
self .timeout_count = 0
44
45
self .is_dead = False
45
46
46
47
def run (self ):
48
+ self .threadpool_executor = ThreadPoolExecutor (max_workers = 6 )
49
+
47
50
policy = asyncio .get_event_loop_policy ()
48
51
policy .set_event_loop (policy .new_event_loop ())
49
52
self .loop = asyncio .new_event_loop ()
@@ -61,7 +64,27 @@ def run(self):
61
64
self .tasks ['report_last_block' ] = self .loop .create_task (self .report_last_block ())
62
65
self .tasks ['report_archival' ] = self .loop .create_task (self .report_archival ())
63
66
self .tasks ['main_loop' ] = self .loop .create_task (self .main_loop ())
64
- self .loop .run_until_complete (self .idle_loop ())
67
+ self .tasks ['idle_loop' ] = self .loop .create_task (self .idle_loop ())
68
+
69
+ finished , unfinished = self .loop .run_until_complete (asyncio .wait ([
70
+ self .tasks ['report_last_block' ], self .tasks ['report_archival' ], self .tasks ['main_loop' ], self .tasks ['idle_loop' ]],
71
+ return_when = asyncio .FIRST_COMPLETED ))
72
+
73
+ self .exit_event .set ()
74
+
75
+ for to_cancel in unfinished :
76
+ to_cancel .cancel ()
77
+ try :
78
+ self .loop .run_until_complete (to_cancel )
79
+ except :
80
+ pass
81
+
82
+ self .threadpool_executor .shutdown ()
83
+
84
+ self .output_queue .cancel_join_thread ()
85
+ self .input_queue .cancel_join_thread ()
86
+ self .output_queue .close ()
87
+ self .input_queue .close ()
65
88
66
89
@property
67
90
def info (self ):
@@ -73,97 +96,81 @@ def info(self):
73
96
'number' : self .ls_index ,
74
97
}
75
98
76
- async def report_dead (self ):
77
- if not self .is_dead :
78
- self .is_dead = True
79
-
80
- format_exc = traceback .format_exc ()
81
- logger .error ('Dead report: {format_exc}' , format_exc = format_exc )
82
- await self .output_queue .coro_put ((TonlibWorkerMsgType .DEAD_REPORT , format_exc ))
83
-
84
99
async def report_last_block (self ):
85
- try :
86
- while not self .is_dead :
87
- last_block = - 1
88
- try :
89
- masterchain_info = await self .tonlib .get_masterchain_info ()
90
- last_block = masterchain_info ["last" ]["seqno" ]
91
- self .timeout_count = 0
92
- except asyncio .CancelledError :
93
- logger .warning ('Client #{ls_index:03d} report_last_block timeout' , ls_index = self .ls_index )
94
- self .timeout_count += 1
95
- except Exception as e :
96
- logger .error ("Client #{ls_index:03d} report_last_block exception: {exc}" , ls_index = self .ls_index , exc = e )
97
- self .timeout_count += 1
98
-
99
- if self .timeout_count >= 10 :
100
- raise RuntimeError (f'Client #{ self .ls_index :03d} got { self .timeout_count } timeouts in report_last_block' )
101
-
102
- self .last_block = last_block
103
- await self .output_queue .coro_put ((TonlibWorkerMsgType .LAST_BLOCK_UPDATE , self .last_block ))
104
- await asyncio .sleep (1 )
105
- except :
106
- await self .report_dead ()
100
+ while not self .exit_event .is_set ():
101
+ last_block = - 1
102
+ try :
103
+ masterchain_info = await self .tonlib .get_masterchain_info ()
104
+ last_block = masterchain_info ["last" ]["seqno" ]
105
+ self .timeout_count = 0
106
+ except asyncio .CancelledError :
107
+ logger .warning ('Client #{ls_index:03d} report_last_block timeout' , ls_index = self .ls_index )
108
+ self .timeout_count += 1
109
+ except Exception as e :
110
+ logger .error ("Client #{ls_index:03d} report_last_block exception: {exc}" , ls_index = self .ls_index , exc = e )
111
+ self .timeout_count += 1
112
+
113
+ if self .timeout_count >= 10 :
114
+ raise RuntimeError (f'Client #{ self .ls_index :03d} got { self .timeout_count } timeouts in report_last_block' )
115
+
116
+ self .last_block = last_block
117
+ await self .loop .run_in_executor (self .threadpool_executor , self .output_queue .put , (TonlibWorkerMsgType .LAST_BLOCK_UPDATE , self .last_block ))
118
+ await asyncio .sleep (1 )
107
119
108
120
async def report_archival (self ):
109
- try :
110
- while not self .is_dead :
111
- is_archival = False
112
- try :
113
- block_transactions = await self .tonlib .get_block_transactions (- 1 , - 9223372036854775808 , random .randint (2 , 4096 ), count = 10 )
114
- is_archival = block_transactions .get ("@type" , "" ) == "blocks.transactions"
115
- except asyncio .CancelledError :
116
- logger .warning ('Client #{ls_index:03d} report_archival timeout' , ls_index = self .ls_index )
117
- except Exception as e :
118
- logger .error ("Client #{ls_index:03d} report_archival exception: {exc}" , ls_index = self .ls_index , exc = e )
119
- self .is_archival = is_archival
120
- await self .output_queue .coro_put ((TonlibWorkerMsgType .ARCHIVAL_UPDATE , self .is_archival ))
121
- await asyncio .sleep (600 )
122
- except :
123
- await self .report_dead ()
121
+ while not self .exit_event .is_set ():
122
+ is_archival = False
123
+ try :
124
+ block_transactions = await self .tonlib .get_block_transactions (- 1 , - 9223372036854775808 , random .randint (2 , 4096 ), count = 10 )
125
+ is_archival = block_transactions .get ("@type" , "" ) == "blocks.transactions"
126
+ except asyncio .CancelledError :
127
+ logger .warning ('Client #{ls_index:03d} report_archival timeout' , ls_index = self .ls_index )
128
+ except Exception as e :
129
+ logger .error ("Client #{ls_index:03d} report_archival exception: {exc}" , ls_index = self .ls_index , exc = e )
130
+ self .is_archival = is_archival
131
+ await self .loop .run_in_executor (self .threadpool_executor , self .output_queue .put , (TonlibWorkerMsgType .ARCHIVAL_UPDATE , self .is_archival ))
132
+ await asyncio .sleep (600 )
124
133
125
134
async def main_loop (self ):
126
- try :
127
- while not self .is_dead :
135
+ while not self .exit_event .is_set ():
136
+ try :
137
+ task_id , timeout , method , args , kwargs = await self .loop .run_in_executor (self .threadpool_executor , self .input_queue .get , True , 1 )
138
+ except queue .Empty :
139
+ continue
140
+
141
+ result = None
142
+ exception = None
143
+
144
+ start_time = datetime .now ()
145
+ if time .time () < timeout :
128
146
try :
129
- task_id , timeout , method , args , kwargs = await self .input_queue .coro_get (timeout = 3 )
130
- except :
131
- continue
132
-
133
- result = None
134
- exception = None
135
-
136
- start_time = datetime .now ()
137
- if time .time () < timeout :
138
- try :
139
- result = await self .tonlib .__getattribute__ (method )(* args , ** kwargs )
140
- except asyncio .CancelledError :
141
- exception = Exception ("Liteserver timeout" )
142
- logger .warning ("Client #{ls_index:03d} did not get response from liteserver before timeout" , ls_index = self .ls_index )
143
- except Exception as e :
144
- exception = e
145
- logger .warning ("Client #{ls_index:03d} raised exception while executing task. Method: {method}, args: {args}, kwargs: {kwargs}, exception: {exc}" ,
146
- ls_index = self .ls_index , method = method , args = args , kwargs = kwargs , exc = e )
147
- else :
148
- logger .debug ("Client #{ls_index:03d} got result {method}" , ls_index = self .ls_index , method = method )
147
+ result = await self .tonlib .__getattribute__ (method )(* args , ** kwargs )
148
+ except asyncio .CancelledError :
149
+ exception = Exception ("Liteserver timeout" )
150
+ logger .warning ("Client #{ls_index:03d} did not get response from liteserver before timeout" , ls_index = self .ls_index )
151
+ except Exception as e :
152
+ exception = e
153
+ logger .warning ("Client #{ls_index:03d} raised exception while executing task. Method: {method}, args: {args}, kwargs: {kwargs}, exception: {exc}" ,
154
+ ls_index = self .ls_index , method = method , args = args , kwargs = kwargs , exc = e )
149
155
else :
150
- exception = asyncio . TimeoutError ( )
151
- logger . warning ( "Client #{ls_index:03d} received task after timeout" , ls_index = self . ls_index )
152
- end_time = datetime . now ()
153
- elapsed_time = ( end_time - start_time ). total_seconds ( )
154
-
155
- # result
156
- tonlib_task_result = TonlibClientResult ( task_id ,
157
- method ,
158
- elapsed_time = elapsed_time ,
159
- params = [ args , kwargs ] ,
160
- result = result ,
161
- exception = exception ,
162
- liteserver_info = self . info )
163
- await self . output_queue . coro_put (( TonlibWorkerMsgType . TASK_RESULT , tonlib_task_result ))
164
- except :
165
- await self .report_dead ( )
166
-
156
+ logger . debug ( "Client #{ls_index:03d} got result {method}" , ls_index = self . ls_index , method = method )
157
+ else :
158
+ exception = asyncio . TimeoutError ()
159
+ logger . warning ( "Client #{ls_index:03d} received task after timeout" , ls_index = self . ls_index )
160
+ end_time = datetime . now ()
161
+ elapsed_time = ( end_time - start_time ). total_seconds ()
162
+
163
+ # result
164
+ tonlib_task_result = TonlibClientResult ( task_id ,
165
+ method ,
166
+ elapsed_time = elapsed_time ,
167
+ params = [ args , kwargs ] ,
168
+ result = result ,
169
+ exception = exception ,
170
+ liteserver_info = self . info )
171
+ await self .loop . run_in_executor ( self . threadpool_executor , self . output_queue . put , ( TonlibWorkerMsgType . TASK_RESULT , tonlib_task_result ) )
172
+
167
173
async def idle_loop (self ):
168
- while not self .is_dead :
174
+ while not self .exit_event . is_set () :
169
175
await asyncio .sleep (0.5 )
176
+ raise Exception ("exit_event set" )
0 commit comments