IRC logs for #buildstream for Monday, 2020-07-13

*** tristan has quit IRC04:07
*** tristan has joined #buildstream05:27
*** ChanServ sets mode: +o tristan05:27
*** hasebastian has joined #buildstream05:34
tristanINTERNALERROR> coverage.misc.CoverageException: Conflicting file tracer name for '/builds/BuildStream/buildstream/src/buildstream/_variables.pyx': 'Cython.Coverage.Plugin' vs ''05:42
tristan:-S05:42
juergbitristan: python 3.8?06:39
*** tristan has quit IRC07:13
*** benschubert has joined #buildstream07:35
*** phildawson has joined #buildstream08:00
*** traveltissues has joined #buildstream08:02
*** tristan has joined #buildstream08:29
*** ChanServ sets mode: +o tristan08:29
tristanjuergbi, CI08:30
tristanjuergbi, I probably messed something out, and think benschubert might have an answer08:31
tristancython is full of little secrets and surprises08:31
juergbiis this for the job with updated dependencies?08:31
juergbiI thought this was an issue only with coverage 5 and/or python 3.808:31
tristanLike for example, sometimes a test fails, and you look in the log, and the exception which your test expects is actually printed there in the stderr08:31
benschuberttristan: which build?08:32
tristanBut cython doesnt say: "Here is the exception we *would* have raised, if you had declared this cdef function without a return value, so we're just printing it to stderr instead"08:32
tristansec08:32
tristanbenschubert, essentially INTERNALERROR> coverage.misc.CoverageException: Conflicting file tracer name for '/builds/BuildStream/buildstream/src/buildstream/_variables.pyx': 'Cython.Coverage.Plugin' vs ''08:32
benschubertAh yeah08:32
benschubertodn't update to coverage 508:33
tristanbenschubert, I didn't, this is in CI08:33
benschubertlink?08:33
tristanhttps://gitlab.com/BuildStream/buildstream/-/pipelines/16548320508:34
tristanI may have messes something up but I don't think I've changed setup.py or tox.ini or setup.cfg08:34
tristanbenschubert, would be cool by the way if you can benchmark the changes I threw up over the weekend08:35
benschubertit's coverage 4.4 indeed, I have no idea why it's failing08:35
tristanbenschubert,  tristan/partial-variables-manual-string-join (tip: 87added5f and before tip: 1757a42d4)08:36
tristanthose appear to make a change in profiling08:36
tristanessentially the dent we discussed on friday08:36
tristancaching the array and struct-izing ResolutionStep08:37
benschuberttristan: let me start a benchmakr08:41
tristanI'll try to figure it out, should be something silly08:41
tristanbenschubert, beware of raising exceptions and return values in cython btw08:42
tristanhttps://cython.readthedocs.io/en/latest/src/userguide/language_basics.html#error-return-values08:43
* tristan got bitten hard08:43
benschubertI know :D08:43
tristanthey should really prefix the error08:43
tristanI'll file an issue against cython08:43
tristan"This is the error which was not raised: {}".format(exception)08:43
tristanotherwise you have no hint to go by08:44
*** santi has joined #buildstream08:50
benschuberttristan: https://gitlab.com/snippets/199531609:03
benschuberttristan: also, for show only it's quite straightforward to run it yourself: git clone https://gitlab.com/Buildstream/benchmarking/bst-benchmarks && cd bst-benchmarks; # replace https://gitlab.com/BuildStream/benchmarking/bst-benchmarks/-/blob/master/benchmark.py#L35 byt "origin/juerg/bst2" and then you can run: tox -- --config normal --show-only your_branch_or_commit09:05
tristanbenschubert, ah thanks !\12:29
tristanI can do them myself then12:30
tristanbenschubert, starting/continuing to think this is a huge wasted time sink, may be better off axing this whole variables journey and living with the recursive algo12:31
benschubertYeah it might be better for now at least12:33
benschubertkeeping some of the documentation/refactor you did in the early rework12:34
benschubertthat helped a lot :)12:34
tristanI don't think there is a performant version of the early rework at all, as it immediately aimed to remove `raise RecursionError`12:42
tristanBut I can add some renames/docs along with valentind's original patch reworked a bit12:42
tristanbenschubert, if you do find time to give the branch a lookover (the code, not the history, that's too much fiddling), and if you can spot anything obvious along the lines of "we're doing more work", I'd love to circle back and nail it12:44
tristannot seeing it myself12:44
benschuberttristan: Sure, I've got other cats to handle right now that sadly require way more focus than I'd like but when I have my brain more free I'll do that :)12:45
tristanI'm not sure also that it's not worth landing at some point, I have a feeling that resolving variables is not the bottleneck of loading 6K elements, and the recursion-proofing can be worth it for an already reasonably performant code path12:45
tristanYes of course12:46
benschuberttristan: Agreed, I'd like to test it on some internal projects first to see if I can see a difference, I do need to do a few changes first though :)12:46
tristanInterestingly the last two benchmarks do show some improvement, and prove that allocating structs manually is measurably more performant than instantiating PyObjects12:48
tristanvalentind, are you around ? I'm wondering if you can help me reason about https://gitlab.com/BuildStream/buildstream/-/issues/135912:49
tristanvalentind, specifically the two stages of the load process12:50
valentindI am around12:53
tristanI'm confused about when there is an ElementFactory available12:56
valentindI would like to see the junction.12:56
tristanhttps://gitlab.com/BuildStream/buildstream/-/blob/master/src/buildstream/_loader/loader.py#L39612:56
tristanSee the junction ?12:57
tristanSo at this point, we do some conditional resolving of options for a junction, otherwise we ensure fully loaded12:57
tristanand then we proceed to create the LoadElement12:57
tristanSec, lemme look where we instantiate junctions12:58
valentindNote that Project.create_element calls the right factory depending on whether it is marked as first or main pass.12:58
tristanYeah, that's where I don't have any factories in either case available12:59
tristanAnd I presume it's my fault a load of commits ago when I introduced `link` element12:59
tristanhttps://gitlab.com/BuildStream/buildstream/-/blob/master/src/buildstream/_loader/loadelement.pyx#L12713:00
tristanvalentind, instantiating the LoadElement for a link results in instantiating the `link` element itself13:01
tristanwhich "worked", until proven to not work13:01
valentindNot sure what link does exactly. But if you want it to be alias a junction it has to behave like one.13:01
tristanRight, it's an alias for anything, most usefully a junction13:01
tristanSo where do we instantiate junctions ?13:01
tristanin _get_loader() I see13:02
valentindSo the few places where have '== "junction"' we should probably do something.13:02
tristanElement._new_from_meta()13:02
tristanI think the only meaningful place is in _load_file_no_deps()13:03
tristanAha13:04
valentindin metalement.py also13:04
valentindsetting self.is_junction13:05
valentindAnd in _frontend/widget.py13:05
valentindAh no, not the last one.13:05
valentindBecause it would never be a link element.13:05
tristanSo I think I found that something here is circular13:05
valentindDirectly.13:05
tristanIf we look at the stack trace on https://gitlab.com/BuildStream/buildstream/-/issues/1359, and concentrate on this area: https://gitlab.com/BuildStream/buildstream/-/blob/master/src/buildstream/_loader/loader.py#L39613:06
valentindAlso metaelement.py is probably not important either. Might never go there. So you are right, I think that place you showed in loader.py.13:06
tristanWe can see that what's happening is that we first try to load an element, and we call self._includes.process(node)13:07
tristanself._includes.process(node) results in trying to load the link, because the problem occurs when performing an include across a linked junction13:08
tristanensure_fully_loaded() would have given us a factory though, I think13:09
tristanOk I think I'm on the right track, I thought you might have an obvious comment about why it was a terrible idea to instantiate an Element from the LoadElement constructor and tell me how to do it properly :)13:10
tristanBut otherwise I think I got it :)13:10
douglaswinshipHas anyone used the "--force" option in bst artifact checkout? I think it might be broken.13:11
valentindIn ensure_fully_loaded, we should move "self._fully_loaded = True" to the end.13:11
valentindAnd assert when we are re entrant.13:12
tristanbenschubert, shoot, the circular reference error reports are something I don't want to live without :'(13:14
tristanvalentind, interesting, I just tried that and it incidentally reports a circular reference hehe13:14
benschuberttristan: right13:14
tristanvalentind, maybe by fixing that properly first I will be able to resolve the rest13:15
valentindI think that would be helpful yes.13:15
tristanyeah, my circular reference is: https://gitlab.com/snippets/199539013:17
tristanWhich shows the same provenance of the include13:17
tristanproject.conf [include line]: circular reference loading subsubproject.bst... already searching for subsubproject.bst at [same line]13:18
tristanvalentind, When you say we should assert not reentrant, you think we should remove the early return for an assertion ?13:32
tristanI dont know13:32
tristanAh, no I get it13:33
tristanWe want to be tolerant of that, but we don't want it to be triggered by the same function13:33
tristanok that helps catch the error a bit earlier indeed13:35
valentindWe should set make sure for example that "self.__is_being_fully_loaded" is False. And then set it right away to true.13:43
valentindThat will detect cycles.13:43
valentindpartially_loaded is for something else I think.13:44
tristanvalentind, that's exactly what I did :)13:45
tristantrying to figure out how this runs now, with a dash of tracing13:45
tristanWe load the toplevel target -> This causes the toplevel project to get "fully loaded" -> This triggers an include processing of the toplevel project at the beginning of _load_second_pass()13:48
tristan-> This include processing includes something across a linked junction boundary `(@): subsubproject-link.bst:file.yml`13:49
tristanby processing the first component of that include path, which is a link, we wind up back in _load_file_no_deps() calling self.project.ensure_fully_loaded() again13:50
tristanI suppose it makes sense for options to be resolved "weakly" when processing a link13:51
tristanlike we do for junctions: `self._first_pass_options.process_node(node)`, however, that still leaves us without an ElementFactory at this stage13:52
tristanvalentind, I think we need to move self._load_element_factories() out of the generalized Project._load_pass()13:54
tristanvalentind, it looks like much of the _load_pass() body would want the options resolved before hand, but I don't know if that is valid for loading the element factories13:54
valentindWhat if you configure plugins in an included file?13:55
tristanAre we allowed to load different plugin types based on conditionals in the plugin origins section depending on options ?13:55
tristanHmmmm13:55
tristanOk so I guess it would be ideal to not do that :-S13:56
tristanI mean, I think we don't want/need factories to be conditional, but includes could be valid13:56
tristanmaybe the include processing should be on demand for that node ?13:56
tristanThere is a simpler, but more brute force approach13:57
tristanWhich is, yucky13:58
tristanvalentind, Never instantiate link elements13:58
tristanOh no shouldn't do that I guess13:58
tristanNeed to have link's resolve variables and conditional statements like any other element13:59
tristanthis is a trickier problem than I expected13:59
* tristan adds test case to !1993 for #135914:04
valentindtristan, well, that depends, do you want to be able to include through a link element?14:04
tristanvalentind, Yes14:05
valentindWell, then they need to have the same restrictions as junctions.14:06
tristandefinitely a valid use case, but... the more I think about this, the more I think that; if it works for a junction it should also work for a link14:06
tristanExactly14:06
tristanvalentind, problem is the code flow, junctions get instantiated elsewhere14:06
tristanLemme double check, but this is what happened last week14:07
tristanI changed the line to shallow resolve to `if kind in ("junction", "link")`, and there was still no factory in sight14:07
tristanahhhh14:08
tristanof course14:08
tristanvalentind, The element also decides not to fully load if it's a junction14:09
* tristan missed that part14:09
tristanok so we can solve it that way14:09
* tristan wonders what kind of `if meta.is_junction` conditions we have around, and if we can change code from "If (you_are_a_pony) { I decide to stroke your fur as a special case }" to: "If (you_want_to_be_stroked) { I shall stoke you thusly }"14:10
tristani.e.: 's/is_junction/requires_fully_loaded_project/g' would be more sensible ?14:11
valentindI do not remember.14:12
tristannope14:13
tristanThere are a ton of conditions all different14:13
tristan(all in element.py)14:13
tristanmaybe rename it to something like `is_virtual`14:13
tristanAll of the cases for is_junction appear to make sense for a link as well14:14
tristanHmmmmmm14:26
tristanInfinite recursion loop14:26
tristansorry infinite *include* recursion loop14:26
tristan(caught by valentind's reentrancy guard, though)14:26
tristanOk, got a fix14:40
tristanvalentind, we cannot move `self._fully_loaded = True` to the bottom of ensure_fully_loaded(), we need to explicitly avoid reentrancy introduced from _load_second_pass()14:41
tristanwhile the added check avoids self reentrancy from the same project14:41
tristanLet's see how this goes: https://gitlab.com/BuildStream/buildstream/-/merge_requests/199314:47
tristanvalentind, I may rename is_junction there, but I'd appreciate you gave that MR a look please :)14:48
tristanI don't know if all the other tests will pass but this one for #1359 appears to be fixed by treating links like junctions14:48
valentindtristan, that looks fine. But I do not understand why self._fully_loaded = True cannot be moved at the end of the function.15:12
tristanvalentind, So there is a reason which flows much like the arrows before; i.e. processing nodes triggers loads; I bet this is the same for straight junctions as it is for links15:45
valentindI think I would need to spend time to understand what is happening there.15:46
tristanhttps://gitlab.com/snippets/199544015:47
tristanThis is some trace I added to loader.py and _project.py15:47
tristanbasically shows what's happening when things are successful15:48
tristanensure_fully_loaded needs the if at the beginning to cancel out recursive reentry15:48
*** hasebastian has quit IRC15:49
tristanThe case where fully_loaded is set at the very end: https://gitlab.com/snippets/199544315:51
tristanRight15:52
tristanvalentind basically fully loading the thing causes one to descend into junctions, because of cross junction includes15:52
tristanvalentind, and then fully loading a project ensures the invariant that it's parent project is fully loaded, bubbling back up15:53
tristanvalentind, it's quite simple when you look at it, but I think it's important that I add a comment to that part15:53
tristanvalentind, commented version: https://gitlab.com/snippets/199544615:56
tristanI'll repost a patch tomorrow with something along those lines included15:56
*** santi has quit IRC17:32
*** santi has joined #buildstream17:36
tristandouglaswinship, to fix the `format-check` error in https://gitlab.com/BuildStream/buildstream/-/pipelines/166012901, you just need to run `tox -e format` and squash the result into your commit18:03
tristanthat picky linter is a result of adopting `black` for formatting consistency18:03
nanonymetristan, I'm curious as to what the exact rules are. Would it have been okay with parameters starting from previous line and ) not being on a separate line?19:18
*** benschubert has quit IRC19:41
*** santi has quit IRC21:24
*** xjuan_ has joined #buildstream22:43
*** toscalix has joined #buildstream22:50
*** toscalix has joined #buildstream23:14
*** toscalix has quit IRC23:17

Generated by irclog2html.py 2.15.3 by Marius Gedminas - find it at mg.pov.lt!