-
jjj333_p (any pronouns)
kinda a dumb question, but only one message / etc stanza comes over the wire at a time, yeah?
-
jjj333_p (any pronouns)
like it doesnt make much sense to thread / delegate the reception and initial handling of messages, because the code shouldnt be the bottleneck here?
-
singpolyma
Depends what you mean. It's a linear text stream yes so you can't thread the parser, but if you do a lot of work or IO for each incoming stanza it may make sense to background that depending
-
jjj333_p (any pronouns)
i guess in a way its kinda determining where to put the separation. im working in go lang which makes it pretty easy to just spin off tasks into their own thread, and database would likely get its own thread or whatnot
-
jjj333_p (any pronouns)
im working on making a wrapper library for simplicity, and as it is im letting the library handling the connection, decoding, routing, etc live in one goroutine and then basically sequentially handling each type of message in its own goroutine (not splitting by chat or anything)
-
qy
you'll probably develop subtle bugs if a newer stanza processes before an older one, and they are related
-
jonas’
jjj333_p (any pronouns), note that RFC 6120 mandates strict in-order processing of stanzas from a single stream.
-
jonas’
so you may put different s2s or c2s links into different tasks/threads, but you mustn't parallelize processing of stanzas from a *single* stream
-
jjj333_p (any pronouns)
i figured it would only be a single stream
-
jjj333_p (any pronouns)
kinda what im doing now is the sdk unmarshals it, and does whatever internal procesing, and then it asynchronously handles off to a "user defined" handler
-
jonas’
(you say unmarshal, I hope you don't use golang's built-in XML parser :-))
-
jjj333_p (any pronouns)
im making a wrapper library to an existing library, so its a couple layers removed
-
jjj333_p (any pronouns)
> (you say unmarshal, I hope you don't use golang's built-in XML parser :-)) nah its using what mellium provides ↺
-
jonas’
ah well, then you can shout at mellium if you run into issues, that's fine :D
-
jjj333_p (any pronouns)
mellium does pretty good at having all the different pieces, but it doesnt really have anything to glue it into one user friendly piece
-
jonas’
so "asynchronously handing off to a 'user defined' handler" is likely problematic
-
jonas’
I'd expect a go channel per stream where you get stanzas.
-
jonas’
that would be the correct primitive.
-
jjj333_p (any pronouns)
if i do that the entire application becomes single threaded, i have to decide some point to break
-
jonas’
what application is this where that matters?
-
jjj333_p (any pronouns)
im planning to incorporate this into a client, so you would expect the ui and db to be kinda asyncrhonous from handling messages
-
jjj333_p (any pronouns)
im exaggerating more than anything but still, feels unnecessary
-
jonas’
well sure you can run UI and DB in separate tasks, and you might even gain something from that
-
jonas’
like, the UI may have to query the database unrelated to XMPP events.
-
jonas’
but the XMPP events should be processed in order (though that matters less for a client than it does for a server)
-
jonas’
I suggest to have an XMPP I/O task, a database task, and a UI task, and couple them via channels of some kind.
-
jonas’
where it does matter a lot in a client is, however, message processing.
-
jjj333_p (any pronouns)
my message is too long
-
jonas’
you'll end up in all kinds of fun hells if you don't process <message/> stanzas in the exact order they were sent.✎ -
jonas’
you'll end up in all kinds of fun hells if you don't process <message/> stanzas in the exact order they were received. ✏
-
jjj333_p (any pronouns)
> you'll end up in all kinds of fun hells if you don't process <message/> stanzas in the exact order they were received. oh im talking past decoding, this is strictly for "userland tasks" ↺
-
jjj333_p (any pronouns)
i tried to send context, but i have to go pastebin it
-
jonas’
jjj333_p (any pronouns), I'm also talking about userland tasks
-
jonas’
think OMEMO
-
jonas’
or MUC for that matter
-
jonas’
or anything related to messages.
-
jonas’
you don't want to accidentally reorder messages in your view.
-
jonas’
users really hate that :)
-
jjj333_p (any pronouns)
ah yeah i wasnt considering those to be "userland"
-
jonas’
what _is_ userland then? :)
-
jjj333_p (any pronouns)
i kinda considered decryption to be apart of the parsing
-
jjj333_p (any pronouns)
> what _is_ userland then? :) i have to fallback to my js roots for convenience but think ```js client.on("message", event => { event.reply(`recieved ${event.body}`) }) ``` ↺
-
jjj333_p (any pronouns)
here, for context. all the way up until that go routine call at the very end everything is within the same goroutine as the connection https://pst.everypizza.im/2SPQ
-
jjj333_p (any pronouns)
> i have to fallback to my js roots for convenience but think > ```js > client.on("message", event => { > event.reply(`recieved ${event.body}`) > }) > ``` to clarify, i consider "userland" to be where the user does actions with the end data. i would kinda expect the library to handle decryption and whatever else and be mostly opaque to the end user ↺
-
jjj333_p (any pronouns)
i.e. you kinda have a "client" object in your code that you can then probe actions on and get messages out of
-
jjj333_p (any pronouns)
> I'd expect a go channel per stream where you get stanzas. do you mean a goroutine? ↺
-
jjj333_p (any pronouns)
because i started out using the mux to hand things into a channel which then each message type got handled syncronously, but i got yelled at in the go room because either youre blocking the "main" routine or youre using a buffered channel which is aparently a nono
-
rako
> because i started out using the mux to hand things into a channel which then each message type got handled syncronously, but i got yelled at in the go room because either youre blocking the "main" routine or youre using a buffered channel which is aparently a nono It's totally fine to handle stanzas synchronously in a goroutine, that's basically what it was made for 😁 just don't do it in the main goroutine ↺
-
wellsfargo@exploit.im
Hello
-
jjj333_p (any pronouns)
> It's totally fine to handle stanzas synchronously in a goroutine, that's basically what it was made for 😁 just don't do it in the main goroutine i mean the whole library / connection is going to end up in a goroutine, because the ui lib i wish to use demands you invoke it from the main goroutine, and then all operations basically have to lock to that goroutine ↺
-
rako
Ah, that lock is unwelcome 😄✎ -
rako
Ah, that second lock is unwelcome 😄 ✏
-
rako
can't you write all changes to db and then lock on the db, as usual ?
-
jjj333_p (any pronouns)
eh it makes it fairly easy, it comes with a built in .Do() which will just silently add the operation to the queue and move on, or .DoAndWait() if you absolutely need to wait for some reason
-
jjj333_p (any pronouns)
> can't you write all changes to db and then lock on the db, as usual ? i was going to keep the db kinda in its own synchronous goroutine, keep everything in its own worlds ↺
-
jjj333_p (any pronouns)
gui gets a goroutine, db gets a goroutine, network gets a goroutine
-
jjj333_p (any pronouns)
i did learn that unless you give a channel buffering, it does block until the item is recieved, so you can use insertions to a goroutine to queue read operations, or something of that sort
-
jjj333_p (any pronouns)
i wish to avoid blocking operations as much as humanly (or machinely?) possible, because i have gotten tired of apps that hang when they need to read something from db or fetch a network resource, or wont let operations go through because the ui needs to update (i have seen all of these)
-
doge
Just spin a separate thread for UI
-
rako
Too bad, the nice thing with goroutines is instead of splitting on domain (as you do) you can split on functionality (get message, send message, post to node, ...), block inside functionality but have functionalities run separately
-
rako
this is possible because goroutines aren't threads/processes, they're extremely lightweight concurrent contexts of executions
-
doge
Do you really need to parallelize, for example, DB writing and stanza processing from the same entity? In most cases, I don't think you need that. You usually want one to finish before the other, both to limit the number of states your program can be in, so it is easier to reason about, and also to provide back pressure.
-
jjj333_p (any pronouns)
> Just spin a separate thread for UI go doesnt work with explicit threads, and the ui library will panic if its not the main goroutine (can be interpreted as the world thread) ↺
-
jjj333_p (any pronouns)
so i just delegate _everything else_ to goroutines
-
jjj333_p (any pronouns)
> Do you really need to parallelize, for example, DB writing and stanza processing from the same entity? In most cases, I don't think you need that. You usually want one to finish before the other, both to limit the number of states your program can be in, so it is easier to reason about, and also to provide back pressure. you can use mutexes and other threadsafe operations to kinda lock and await for other threads to get done with things ↺
-
jjj333_p (any pronouns)
so that way you arent prevented from downloading something while the db is writing something irrelevant
-
doge
>> Just spin a separate thread for UI > go doesnt work with explicit threads, and the ui library will panic if its not the main goroutine (can be interpreted as the world thread) I think all major ui libs have a method You can call from any thread that will schedule code to run on the UI's thread. idle_add or smth like that. ↺
-
jjj333_p (any pronouns)
> I think all major ui libs have a method You can call from any thread that will schedule code to run on the UI's thread. idle_add or smth like that. yes i explained that earleir this one has that ↺
-
jjj333_p (any pronouns)
but you have to execute window.ShowAndRun() from the main routine which blocks until it closes
-
rako
> Do you really need to parallelize, for example, DB writing and stanza processing from the same entity? In most cases, I don't think you need that. You usually want one to finish before the other, both to limit the number of states your program can be in, so it is easier to reason about, and also to provide back pressure. It's not about parallelizing, it's about contextualizing "things that make sense together". Not a write from a db and an update in the ui, but a click on the "send" button and a click on another chat, for example. ↺
👍 1 -
moparisthebest
I gotta say that doesn't make a lot of sense from a client point of view, think about this common thing that happens: > user writes a message in a box, hits send
-
moparisthebest
You can't fire that off to independent workers, one who writes to the database and one who sends across the network, because the network might be down or the process might be killed after network send and before database write succeeds
-
lovetox
Sounds all horrible, it's complicated enough to find out what's going on in a fairly advanced client which can reach 100k loc easy
-
lovetox
If you have x asynchron routines/threads I think you will be the only person to touch that project. Just my 2 cents
-
moparisthebest
Frankly this is why general libraries never get used by clients, too many assumptions that prove incorrect in real world use
-
lovetox
In Gajim everything is in one single thread, UI has high prio, network lower prio, if there are CPU intensive ui operations, pulling from the network simply gets Stalled a bit
-
lovetox
Except of course stuff like reading big files or downloading files, these are put into small shortlived Threads for the specific task, but they are not related to the xmpp stream, think download file with http, load Avatars From Disk at start
👍 1 -
lovetox
Most ui blocks are because the UI has too much to do, and the UI can not draw in another thread as you discovered, needs to be in the main thread
-
halscode
> You can't fire that off to independent workers, one who writes to the database and one who sends across the network, because the network might be down or the process might be killed after network send and before database write succeeds mine puts it into local echo (which is a synchronous in-memory object) and network immediately, and once I hear back from the network it pulls back from the local echo, and writes to the database. The database write is completely async and unawaited (though with an error handler attached for debugging, in case something exotic happens), so the user can go right to composing the next message ↺
-
moparisthebest
halscode: so what happens if they write 3 messages while the network is down and then shut down the computer ?
-
halscode
> halscode: so what happens if they write 3 messages while the network is down and then shut down the computer ? right now, enqueued messages are lost. it's pretty predictable from the end-user side, since the local-echoes that haven't been acked yet have a pulsing effect. Persisting it to disk, especially when I expect it to not stay in local echo for very long at all during normal use, is a real waste of resources. Though thanks for pointing that out, that is still an area I can improve on ↺
-
halscode
edge cases do need to be considered
-
moparisthebest
It sounds like sometimes they've sent over the network and sometimes not and they might end up in the database and sometimes not
-
halscode
oh also, I think if the network is down it'll immediately fail, that's another thing to fix... in that case, the queue would be immediately persisted to disk and removed from the database once you're back online and it's sent. Also requires more explicit messaging than just "waiting to see if it's been sent or not"
-
moparisthebest
Basically you need to decide if you want at-least-once (meaning sometimes more) or at-most-once (meaning sometimes never) delivery, I think without doing crazy mam things you can't get exactly-once delivery
-
halscode
If it fails to deliver, it fails with an error message, or it's the/a server's fault for fumbling the message and not mine lol
-
halscode
and some retry conditions, though I'm not sure I actually have that
-
moparisthebest
I suspect for a client you'd want at-least-once, which implies commit to database first with a flag that means not-sent-yet, then keep trying to send and once successful flip the flag to sent
-
halscode
that's true, but I also don't want to write to disk and then delete it again in .1s✎ -
halscode
that's true, but I also don't want to write to disk and then delete it again in .1s if it fails ✏
-
moparisthebest
If it succeeds? But also why not?
-
halscode
disk i/o abuse, mostly. though, ugh, errors of type cancel shouldn't be that common so it's not a very important case
-
moparisthebest
disk i/o isn't really going to be a consideration here, I would be much more annoyed by lost messages
-
halscode
I think part of it was that originally I wasn't using a proper database so I couldn't just delete failed messages, I'd have to reconstruct the whole conversation history to just leave that one out
-
moparisthebest
You almost surely don't want to delete them, you want to keep retrying or mark them failed so the user can retry or at least know they didn't send no?
-
halscode
They shouldn't stay permanently, they're failed, they're irrelevant to the rest of the conversation. Currently they are shown in red next to an error message, until you restart the app. (I think app/window close would be a good time to clean up failed messages from the database, so it doesn't disappear while someone's reading it.)
-
moparisthebest
so I'm trying to send an important message and having trouble getting internet to work and reboot my device in an attempt to fix it and the message(s) are just gone? I would hate that
-
moparisthebest
This actually has happened to be but with Conversations they are marked not sent with the error and I can select "resend" if I want✎ -
moparisthebest
This actually has happened to me but with Conversations they are marked not sent with the error and I can select "resend" if I want ✏
-
halscode
> so I'm trying to send an important message and having trouble getting internet to work and reboot my device in an attempt to fix it and the message(s) are just gone? I would hate that yeah, there's a reason I haven't published this thing yet 🥲️ ↺
-
halscode
These are good conversations to have, for whatever reason it's something I hadn't considered, so it's good that it's being brought up. Thanks, moparisthebest
👍 1 -
Goot the ticklegoblin!
Sometimes you gotta have someone play doubles abdicate with you
-
halscode
So maybe it only gets auto-deleted on explicitly cancel-type errors, that can't be retried (network errors would be treated like "wait")✎ -
halscode
So maybe it only gets auto-deleted on explicitly cancel-type errors, that can't be retried (network errors would be treated like "wait"). You can't resend it if, say, you tried to send something to a MUC you're not allowed to send messages in, or if your contact doesn't exist for some reason ✏
-
moparisthebest
To complicate things more I'd say not even then, I often send messages to a MUC and it can't be delivered because remote-server-not-found , because they are having maintenance or some other thing, and again it's nice to long-press and click resend when they come back
-
moparisthebest
> These are good conversations to have, for whatever reason it's something I hadn't considered, so it's good that it's being brought up. Thanks, moparisthebest 👍 ↺
-
moparisthebest
Yea it's hard/impossible to think of all these things unless you've experienced them lol, happy to play rubber duck who's seen things 🦆☠️