-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HH-196544 add dependency graph #658
Conversation
https://github.com/hhru/frontik/blob/master/docs/preprocessors.md Вот тут очень нехватает изменений, чтобы отражало текущую ситуацию |
frontik/dependency_manager.py
Outdated
self.finished = False | ||
self.task: asyncio.Task | None = None | ||
|
||
async def run(self, handler: PageHandler, topological_sorter: TopologicalSorter) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Есть вопрос ко всех схеме по поводу её самописности, но тут коммент к конкретному месту:
имхо хендлер не надо здесь отдельный параметром делать, думаю лучше, чтобы была какая-то зависимость, сразу зарезолвленная, которая вернет CurrentHandler
frontik/dependency_manager.py
Outdated
|
||
if asyncio.iscoroutinefunction(self.func): | ||
if self.task is None: | ||
self.task = asyncio.create_task(self.func(*self.args)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
но ведь может быть и async function которая является синхронной зависимостью?
Например когда внутри есть какие-то походы, но их надо дождаться?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
тут следующей строчкой сразу же await этой таски
или ты о чем?
таска нужна только из-за субграфа (а он только из "маст_би") чтобы если один граф уже стартанул зависимость, второй не запускал новую, а авейтил существующую
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
хм, она ж тогда финишед станет и следующий граф не возьмет, это похоже надо убрать,
во время разработки какие-то проблемы были
frontik/dependency_manager.py
Outdated
topological_sorter.done(self) | ||
return | ||
|
||
for i in range(len(self.args)): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
а есть причина, почему тут не for arg, i in enumerate(self.args)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
потому что модифицируем self.args в рамках цикла
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ну так ты получаешь тот же самый индекс и можешь итерироваться, просто тебе не нужно писать лишнюю строчку arg = self.args[i]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
я только перепутал, наоборот: for i, arg in enumerate(self.args):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
frontik/dependency_manager.py
Outdated
return make_full_name(self.func) | ||
|
||
|
||
def dep(dependency: Preprocessor | Callable | list[Callable]) -> Any: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
А назови функцию, чтобы было понятно что она делает, не экономь на буквах
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
это название потом в сервисы в аргументы поедет, думаешь удобно будет если оно длинное?
т.е. условно в xhh
@preprocessor
async def vacation_check(handler: PageHanler, session=make_dependency(get_session))
vs
@preprocessor
async def vacation_check(handler: PageHanler, session=dep(get_session))
frontik/dependency_manager.py
Outdated
arg = self.args[i] | ||
if isinstance(arg, Dependency): | ||
if not arg.finished: | ||
msg = f'Graph corrupted, run {self}, before finishing {arg}' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
А зачем эта строчечка в отдельной переменной? Там за лимит длины строки чтоли вылазим? Может надо чот подтюнить?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
это руфф поавтофиксил, https://docs.astral.sh/ruff/rules/f-string-in-exception/
типа дважды потом сообщение в логи
мне в целом не принципиально, можно вырубить
frontik/dependency_manager.py
Outdated
return DependencyMarker(dependency) | ||
|
||
msg = 'Bad dependency type, only func or list[func]' | ||
raise ValueError(msg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Тут точно не вылазим за длинну строки, зачем тут переменная?
frontik/dependency_manager.py
Outdated
done, pending = await asyncio.wait(pending_tasks, return_when=asyncio.FIRST_COMPLETED) | ||
for d in done: | ||
if d.exception() is not None: | ||
raise d.exception() # type: ignore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
А почему тут тип заигнорен?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
я не смог победить mypy)
он говорит Exception must be derived from BaseException [misc]
посмотрел в некоторых либах так делают
frontik/dependency_manager.py
Outdated
|
||
def get_dependency_graph(page_method_func: Callable, handler_cls: type) -> DependencyGraph: | ||
""" | ||
build meta_graph or take ready deepcopy as main_graph |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or take ready deepcopy
это как?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
имел в виду, что у нас есть что-то типа статического объекта
my_module.MyPageHandler.get_page
и если у него нету ._dep_graph, то мы новый строим, проверяем циклы
а если уже есть то делаем deepcopy этого ._dep_graph
чтобы модифицировать, отмечать кого выполнили, кого нет
frontik/dependency_manager.py
Outdated
func_preprocessors = get_preprocessors(page_method_func) | ||
handler_dependencies = getattr(handler_cls, 'dependencies', []) | ||
|
||
analyze_dependency_params(meta_graph, root_dep, False, True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
А давай везде, где ты булевые аргументы передаешь — передадим их как kwargs?
frontik/dependency_manager.py
Outdated
for dependency in chain(func_preprocessors, handler_dependencies): | ||
register_sub_dependency(meta_graph, root_dep, dependency, False, True) | ||
|
||
analyze_dependency_params(meta_graph, root_dep, True, False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Давай переименуем метод, чтобы было понятно, что он не только что-то анализирует, но и меняет что-то внутри аргументов
frontik/dependency_manager.py
Outdated
handler_dependencies = getattr(handler_cls, 'dependencies', []) | ||
|
||
analyze_dependency_params(meta_graph, root_dep, False, True) | ||
for dependency in chain(func_preprocessors, handler_dependencies): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Я правильно понимаю, что в одном цикле нельзя как раз из-за того, что нужно analize_dependency_params вызвать, которая stateful, и состояние может поменяться между вызовами?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
думаю этот вопрос связан со следующим про tier1, подумаю как там получше коммент написать
в целом зависимости в некотором смысле синглтоны, мы по имени функции можем получить объект типа Dependency. Т.е. если в графе много раз встречается какой-нибудь get_session это будет единая зависимость, которая выполнится один раз
Но бывает такое что у нас не нормальная зависимость, а "полученная из фабрики" типа такого
def preproc_factory(params):
@preprocessor
def real_preproc(handler):
pass
return real_preproc
Такая фабрика выдает разные функции с одним именем. В графе может несколько раз в качестве препроцессора/зависимости встретиться вызов фабрики. Выполнять в итоге несколько раз?
В старой схеме выполняли 1 раз если одно имя.
Поэтому сделал так что если уже регистрировали в графе что-то с конкретным именем, то следующие игнорируем. Чтобы мочь управлять какая конкретно зарегистрируется (где-то было нужно), сделал так, что то что явно указано в сигнатуре (а не в глубинах графа) имеет больший приоритет
Чтобы собрать сначала приоритетные фабрики, нужно пройтись по верхам не заходя вглубь. А полный рекурентный обход сначала в ветви идет, так что два прогона, плоский и глубокий
frontik/dependency_manager.py
Outdated
add link to graph | ||
deep scan sub_dep parameters | ||
if there are two different dependency with same name (factory's dep), then leave only first | ||
if they both from tier1 signature raise ValueError('Dependency conflict') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Что такое tier1 signature ?
Была просьба добавить этот текст Q
A
|
54f34ea
to
2cdc0c4
Compare
SonarQube Quality Gate |
https://jira.hh.ru/browse/HH-196544