garrison

garrison

The problem here is not so much optimization of software performance (though of course that matters) but rather optimization of developer performance. That is: not turning your codebase into a spaghetti disaster. We are all programmers on here and we all know what this feels like, so I don’t need to justify this further.

The question the OP actually asked is a question of resource management by components. How can a component allocate a resource (in this case PubSub, but it could be literally anything) and then release it when the component is unmounted. Ideally declaratively and idiomatically within LiveView.

What I have explained (hopefully clearly, but maybe not idk) is that it is not possible to solve this properly in LiveView because components (including the root LV, which is also a component) do not have an unmount callback. It has been proposed several times but so far no dice.

I want to be very clear: there is nothing wrong with your or @krasenyp 's suggestion to filter events on the receiving end. This is a legitimate technique, and that’s what I was getting at with the Slack bit at the end. There are times when you can and should do this.

However, the filtering trick does not solve the problem in the OP. The inability to manage resources will come back to haunt you either way. There is a reason most frameworks have some sort of unmount. React post-Hooks came up with something even better (the useEffect continuation/slicing trick) which I also described, and which is also described in much more detail in one of those articles I linked.

I will try to give one last example. Say you have a LiveView at /products, like the OP, and it receives events. Now say you have another completely unrelated page in your app, like /settings. You make use of the live navigation feature to allow users to navigate between pages over the socket (as the OP did).

A user:

  • Loads /products/1 and mounts a Product LiveView
  • Subscribes to the "product_updates" PubSub channel
  • Clicks on a link to /settings which mounts the Settings LV in the same process

The Settings LiveView now receives product updates.

Put aside all performance concerns here and just trust your experience. Can you honestly say that you think this sounds like a good idea? Can you honestly say that down the road, when your app has 100 pages, a bunch random events bouncing around unpredictably between all of them won’t cause confusion or bugs?

It is not premature optimization to design systems properly from the start, and this is not an edge case. There is no easy way to paper over this with some hack, because it will always come back. It will come back with components, it will come back with other types of resources, and so on.

The OP asked for an elegant solution and I don’t believe there is one. If they had asked about React (or really any other app framework) I could have just handed them the useEffect() example and that would be it.

If anyone else has an actual solution that I haven’t thought of please post it. Filtering, while entirely valid, does not solve this problem.

1
Post #11
tfwright

tfwright

If the problem you’re referring to is not one software performance, then I don’t think there is one, unless maybe the hard problem of naming. As @krasenyp already said above, the scenario as stated by OP is a common problem and this solution does in fact work. Whether it’s the “best” solution, or even good is a much, well, harder problem. So if your question “Can you honestly say that you think this sounds like a good idea?” is genuine, then yes, I can. If it’s rhetorical, I can just ask you the inverse one, which is why we should avoid rhetorical questions.

I quite frankly don’t think this is a meaningful statement. If “premature optimization” has any meaning at all, it is precisely that “from the start” you don’t know what the “proper” design for the system will be in the future, and so you need to attempt to constrain your design to the problems that are known. Like anything else of significance, this is not necessarily clear or easy, and indeed I do think it falls under a broad interpretation of the problem of naming (i.e. that names directly reflect the design and can only be as “good” as the design itself).

edit

From what you’ve written elsewhere I would guess your real gripe is with the design of LiveView itself in view of its ability (or lack of it) to elegantly handle the problems most web apps eventually face more than it is with the “filtering strategy” for handling PubSub messages. And in that regard I’m actually pretty sympathetic to your view. Just think it’s a bit off topic in this thread where LV is not on trial and really is not very related to this problem, since any system with processes needing to ingest data from some set of PubSub channels would face a similar problem.

1
Post #12
garrison

garrison

If you are writing genuinely exploratory code then it is indeed much harder to structure everything correctly the first time.

However, if you are working with established patterns you can absolutely lean on past experiences. Both your own, and those of others. In the first section of the useEffect docs React gives a proper example of how to manage a resource. Clearly this is not an obscure problem but an extremely common one with known solutions.

Please re-read the OP. You will see that it very clearly describes this exact problem.

Again: if there is something I have overlooked, I would very much like to hear about it. But as far as I can tell it is not possible to manage a resource in LiveView at all.

“Allocate the resource and then never free it” is not a solution to this problem. Freeing the resource by terminating the LiveView process is also not a solution to this problem, as I have now demonstrated three separate times.

This does not mean I take any issue with either of you suggesting the filtering trick. It’s relevant! It just doesn’t solve the problem.

0
Post #13
garrison

garrison

I have gripes with everything. LiveView is fine.

In this case, the low-hanging-fruit solution would be to add an unmount callback to LiveViews and LiveComponents. Note that I did not advocate for this in my first reply (which was days ago), but I have since thought about it more and I think clearly an unmount is just fundamentally necessary.

I strongly disagree that this is off-topic.

1
Post #14
rhcarvalho

rhcarvalho

Navigating between LiveViews starts a new process. Any subscription a previous LV had are gone as part of the BEAM process model.

The only kind of navigation within the same process is live patch (which is probably the wrong design choice for navigating between different product pages).

3
Post #15
tfwright

tfwright

OK, yeah maybe that’s too strong given LV is in the OP, but I do think a general discussion about what features LV doesn’t have or should have is at best tangential to actually answer the question which is about how to use the tools it does have.

An added complication is that I think this is an XY problem because with the filtering strategy you don’t care about the lifecycle at all because the subscription no longer needs to be managed. When the product page live view is running, it’s receiving product events. The handler will sort out what to do with them (if anything). Still not sure about your complaints with that suggestion.

1
Post #16
garrison

garrison

Oh, does it? My bad, I thought it remounted in the same process, but you’re probably right. Fortunately that example is not load-bearing because this problem is still exhibited everywhere else. I was just trying to find something that was irrefutable even in the simplest possible case.

And yet:

But really, I disagree that this is a bad idea. Actually doing everything within one LiveView is a great idea. There is no reason to reload all of the parts of the page that are shared between products (discarding all state in the process). The whole point of LiveView is that it’s supposed to be richer than a regular HTML page!

1
Post #17
garrison

garrison

You’re right, but the problem is that you think it’s an XY problem and I happen to think the OP’s approach is actually correct.

Again, there’s nothing wrong with doing the filtering thing. It’s just that the question from the OP, “how do I manage resources”, is going to come back anyway. Honestly, I tried pretty hard to explain it, but maybe there is no substitute for suffering through this yourself. I have been through that many times, where I thought others were just over-complicating things until I actually felt enough pain to understand the justification.

0
Post #18
tfwright

tfwright

Except I am not the only one in this thread pointing out that it doesn’t necessarily involve any “suffering” at all. It works fine, except in the hypothetical scenarios you introduced. I guess I just don’t find them convincing. Whatever approach you take to designing your topics, there are going to be places where they need to be filtered, unless you decide to have a topic for every single client’s use case. So it’s really just about finding the balance between the number of topics that need subscriptions, and the number of events that will be pushed to each topic, based on known use cases. The issue of managing resources may or may not come back, but not because of OPs problem.

But even this is off topic at this point, because it’s since been pointed out that the real XY problem is that a new LV process should be mounting for each product anyway, so I’ll leave it there.

1
Post #19
garrison

garrison

I can only agree with you on this claim so many times lol. A further point in your favor here: due to consistency problems with PubSub, you are going to have to filter the messages anyway! This does not, however, contradict my points in any way.

Yes! And unless the number of topics happens to be exactly one, an uncommon case, we have invented the OP’s question from first principles.

Okay, imagine you have literally any other component on the page. A search bar, a sidebar with filters, literally anything at all. You click on another product and the state of every other control on the page gets reset. This is a common annoyance of web applications.

LiveView is useful specifically because it can handle this, in Elixir, with no client rendering. That is the value proposition!

What is frustrating to me here is that the OP has the right idea, and is doing the right thing, and all of you are telling them “you’re holding it wrong”. They are not!

0
Post #20