1
1
import copy
2
- import os
3
2
from collections import OrderedDict , defaultdict
4
3
from contextlib import ExitStack
5
4
from functools import wraps
@@ -143,30 +142,81 @@ def to_mermaid(self, left_right: bool = True):
143
142
Output the mermaid graph for visualization
144
143
145
144
:param left_right: render the flow in left-to-right manner, otherwise top-down manner.
146
- :return:
145
+ :return: a mermaid-formatted string
147
146
"""
147
+
148
+ # fill, stroke
149
+ service_color = {
150
+ Service .Frontend : ('#FFE0E0' , '#000' ),
151
+ Service .Router : ('#C9E8D2' , '#000' ),
152
+ Service .Encoder : ('#FFDAAF' , '#000' ),
153
+ Service .Preprocessor : ('#CED7EF' , '#000' ),
154
+ Service .Indexer : ('#FFFBC1' , '#000' ),
155
+ }
156
+
148
157
mermaid_graph = OrderedDict ()
149
- for k in self ._service_nodes .keys ():
150
- mermaid_graph [k ] = []
151
158
cls_dict = defaultdict (set )
159
+ replicas_dict = {}
160
+
161
+ for k , v in self ._service_nodes .items ():
162
+ mermaid_graph [k ] = []
163
+ num_replicas = getattr (v ['parsed_args' ], 'num_parallel' , 1 )
164
+ if num_replicas > 1 :
165
+ head_router = k + '_HEAD'
166
+ tail_router = k + '_TAIL'
167
+ replicas_dict [k ] = (head_router , tail_router )
168
+ cls_dict [Service .Router ].add (head_router )
169
+ cls_dict [Service .Router ].add (tail_router )
170
+ p_r = '((%s))'
171
+ k_service = v ['service' ]
172
+ p_e = '((%s))' if k_service == Service .Router else '(%s)'
173
+
174
+ mermaid_graph [k ].append ('subgraph %s["%s (replias=%d)"]' % (k , k , num_replicas ))
175
+ for j in range (num_replicas ):
176
+ r = k + '_%d' % j
177
+ cls_dict [k_service ].add (r )
178
+ mermaid_graph [k ].append ('\t %s%s-->%s%s' % (head_router , p_r % 'router' , r , p_e % r ))
179
+ mermaid_graph [k ].append ('\t %s%s-->%s%s' % (r , p_e % r , tail_router , p_r % 'router' ))
180
+ mermaid_graph [k ].append ('end' )
181
+ mermaid_graph [k ].append (
182
+ 'style %s fill:%s,stroke:%s,stroke-width:2px,stroke-dasharray:5,stroke-opacity:0.3,fill-opacity:0.5' % (
183
+ k , service_color [k_service ][0 ], service_color [k_service ][1 ]))
152
184
153
185
for k , ed_type in self ._service_edges .items ():
154
186
start_node , end_node = k .split ('-' )
187
+ cur_node = mermaid_graph [start_node ]
188
+
155
189
s_service = self ._service_nodes [start_node ]['service' ]
156
190
e_service = self ._service_nodes [end_node ]['service' ]
191
+
192
+ start_node_text = start_node
193
+ end_node_text = end_node
194
+
195
+ # check if is in replicas
196
+ if start_node in replicas_dict :
197
+ start_node = replicas_dict [start_node ][1 ] # outgoing
198
+ s_service = Service .Router
199
+ start_node_text = 'router'
200
+ if end_node in replicas_dict :
201
+ end_node = replicas_dict [end_node ][0 ] # incoming
202
+ e_service = Service .Router
203
+ end_node_text = 'router'
204
+
205
+ # always plot frontend at the start and the end
206
+ if e_service == Service .Frontend :
207
+ end_node_text = end_node
208
+ end_node += '_END'
209
+
157
210
cls_dict [s_service ].add (start_node )
158
211
cls_dict [e_service ].add (end_node )
159
212
p_s = '((%s))' if s_service == Service .Router else '(%s)'
160
213
p_e = '((%s))' if e_service == Service .Router else '(%s)'
161
- mermaid_graph [start_node ].append ('\t %s%s-- %s -->%s%s' % (
162
- start_node , p_s % start_node , ed_type ,
163
- end_node , p_e % end_node ))
164
-
165
- style = ['classDef FrontendCLS fill:#FFE0E0,stroke:#FFE0E0,stroke-width:1px;' ,
166
- 'classDef EncoderCLS fill:#FFDAAF,stroke:#FFDAAF,stroke-width:1px;' ,
167
- 'classDef IndexerCLS fill:#FFFBC1,stroke:#FFFBC1,stroke-width:1px;' ,
168
- 'classDef RouterCLS fill:#C9E8D2,stroke:#C9E8D2,stroke-width:1px;' ,
169
- 'classDef PreprocessorCLS fill:#CEEEEF,stroke:#CEEEEF,stroke-width:1px;' ]
214
+ cur_node .append ('\t %s%s-- %s -->%s%s' % (
215
+ start_node , p_s % start_node_text , ed_type ,
216
+ end_node , p_e % end_node_text ))
217
+
218
+ style = ['classDef %sCLS fill:%s,stroke:%s,stroke-width:1px;' % (k , v [0 ], v [1 ]) for k , v in
219
+ service_color .items ()]
170
220
class_def = ['class %s %sCLS;' % (',' .join (v ), k ) for k , v in cls_dict .items ()]
171
221
mermaid_str = '\n ' .join (
172
222
['graph %s' % ('LR' if left_right else 'TD' )] + [ss for s in mermaid_graph .values () for ss in
@@ -175,19 +225,30 @@ def to_mermaid(self, left_right: bool = True):
175
225
return mermaid_str
176
226
177
227
@_build_level (BuildLevel .GRAPH )
178
- def to_jpg (self , path : str = 'flow.jpg' , left_right : bool = True ):
228
+ def to_url (self , ** kwargs ) -> str :
229
+ """
230
+ Rendering the current flow as a url points to a SVG, it needs internet connection
231
+
232
+ :param kwargs: keyword arguments of :py:meth:`to_mermaid`
233
+ :return: the url points to a SVG
234
+ """
235
+ import base64
236
+ mermaid_str = self .to_mermaid (** kwargs )
237
+ encoded_str = base64 .b64encode (bytes (mermaid_str , 'utf-8' )).decode ('utf-8' )
238
+ return 'https://mermaidjs.github.io/mermaid-live-editor/#/view/%s' % encoded_str
239
+
240
+ @_build_level (BuildLevel .GRAPH )
241
+ def to_jpg (self , path : str = 'flow.jpg' , ** kwargs ):
179
242
"""
180
243
Rendering the current flow as a jpg image, this will call :py:meth:`to_mermaid` and it needs internet connection
181
244
182
245
:param path: the file path of the image
183
- :param left_right: render the flow in left-to-right manner, otherwise top-down manner.
246
+ :param kwargs: keyword arguments of :py:meth:`to_mermaid`
184
247
:return:
185
248
"""
186
- import base64
249
+
187
250
from urllib .request import Request , urlopen
188
- mermaid_str = self .to_mermaid (left_right )
189
- encoded_str = base64 .b64encode (bytes (mermaid_str , 'utf-8' )).decode ('utf-8' )
190
- print ('https://mermaidjs.github.io/mermaid-live-editor/#/view/%s' % encoded_str )
251
+ encoded_str = self .to_url ().replace ('https://mermaidjs.github.io/mermaid-live-editor/#/view/' , '' )
191
252
self .logger .info ('saving jpg...' )
192
253
req = Request ('https://mermaid.ink/img/%s' % encoded_str , headers = {'User-Agent' : 'Mozilla/5.0' })
193
254
with open (path , 'wb' ) as fp :
@@ -226,8 +287,6 @@ def query(self, bytes_gen: Iterator[bytes] = None, **kwargs):
226
287
227
288
@_build_level (BuildLevel .RUNTIME )
228
289
def _call_client (self , bytes_gen : Iterator [bytes ] = None , ** kwargs ):
229
- os .unsetenv ('http_proxy' )
230
- os .unsetenv ('https_proxy' )
231
290
args , p_args = self ._get_parsed_args (self , set_client_cli_parser , kwargs )
232
291
p_args .grpc_port = self ._service_nodes [self ._frontend ]['parsed_args' ].grpc_port
233
292
p_args .grpc_host = self ._service_nodes [self ._frontend ]['parsed_args' ].grpc_host
0 commit comments