Re: Roster module with custom MySQL requests

2014-04-29 Thread Sylvain Guglielmi

Le 01/04/2014 14:23, Tomasz Sterna a écrit :

Dnia 2014-04-01, wto o godzinie 12:14 +0200, Sylvain Guglielmi pisze:

The problem is :
- When an user connects to jabberd2 for the first time, the
active,logout... tables are empty, and the roster is already filled.
- The code in dispatch.c : 130 states that if one of the user_load
module fails, an unsuscribed-packet will be sent, presumably to clean
the user's rosters on every session by removing this unknown
contact.

Just remove 'active' module from 'user-load' chain in your sm.xml.


Unfortunately it does not work : When I remove 'active' module from 
'user-load' chain, the auto-create in sess.c does not trigger since 
user_load returned sucessfully. user_create is never called for new 
users. This causes other issues (For example, active is never filled 
for the user. My search module relies on it, and you said that offline 
messages checked that too, and I'd not be surprised if others modules do 
too).


It seems to me that it's related to something you stated earlier : there 
should be differents chains for user_load, the one when an user connects 
(called from sess.c) and a quicker one called from dispatch and other 
locations too (which should not load roster, but only plain info such as 
active or routing info).


Sadly, I have not the time now for doing all that. I think my best 
option is to disable the presence unsubscribed in dispatch.c (via a 
config option). I'd gladly accept any better solution. When I'll have 
more time, I'll get back on doing the right thing.


Thanks in advance for any insight.


--
Sylvain Gugli Guglielmi
Gamedev@Nadeo@Ubisoft





Re: Roster module with custom MySQL requests

2014-04-01 Thread Sylvain Guglielmi

Hello again everyone,

I have some questions concerning dispatch.c, but first a short recap of 
the plugin I'm working on :
- the plugin replaces roster and allows to write custom sql requests 
for accessing data (instead of the storage component). The purpose is to 
be able to use our already-existing buddies database, read-and-write.
- I enabled auto-create/ : the server's other tables (active, 
logout...) get populated at the user's first connection


The problem is :
- When an user connects to jabberd2 for the first time, the 
active,logout... tables are empty, and the roster is already filled.
- The code in dispatch.c : 130 states that if one of the user_load 
module fails, an unsuscribed-packet will be sent, presumably to clean 
the user's rosters on every session by removing this unknown contact.
- In my case, when an user connects but one of his buddies has never 
been connected yet, the active module fails to user_load. The 
unsuscribed packet is generated, and the buddy-relationship is erased 
from the roster, unfortunately losing the relationship.


I see three solutions here :
- using the template-roster module, or adapting it, i could maybe 
recreate the buddies-relationships after they are deleted. But it would 
mean i have to keep two tables : roster-before-first-connection, and 
roster-after-first-connexion(=real roster), which seems weird. Also, it 
would mean a lot of unneeded operations on first connection.
- populate all the tables of the server (active, status...) in advance 
(i.e before the first connection to XMPP is even possible). It makes 
more sense, because it avoid to have half-created sessions (with only 
a valid roster but nothing else). But it's also a certain amount of work 
for me, and server load on the future (to keep every table of the SM's 
database up-to-date regarding account creations).
- add a config flag in sm : allow relationship to not-yet-existing 
buddies that would prevent the unsuscribed packet form being sent.


I think the most sensible way is the last one, but I may miss some 
issues. Any insight would be greatly welcome, as usual.


Thanks for reading me, and in advance for any answer ^_^.







Le 17/01/2014 11:59, Sylvain Guglielmi a écrit :

Le 10/01/2014 18:23, Tomasz Sterna a écrit

You could have a separate cron component pinging 'sm' in regular
intervals with special route packet, and handle this special packet in
'in-router' chain of your module.

Having that it could even be done not in regular intervals, but
on-demand, when your component gets triggered by web frontend.





This works very well ! Thanks for the advice. My cron component is a 
short php script.
I was unsure about how to answer disco#info queries that I get from 
other components, but it seems to work without answering anything. 
I'll read the XEP-0030 : Service Discovery RFC later and try to make 
a more compliant cron component.
If anyone is interested in the php simple DIGEST-MD5 auth, code can be 
found here (It's not robust and exhaustive, but it can be useful I 
guess) :

https://github.com/Gugli/jabberd2/blob/master/tools/sendupdatepacket.php

I've been scratching my head for the last couple of days on another 
issue : our legacy contact system does not allow asymetrical 
relationships (as XMPP does). It would be very complicated to change 
that to a more XMPP-compliant system. The solution I came up with is 
to make a symmetrical mode for my roster module.

In this mode :
- whenever a user sends a presence subscription, the sm also consider 
the user allows subscription to the same contact.
- whenever a user allows a contact to subscribe to his presence, the 
sm also consider that the user subscribe back to the same contact's 
presence.
The session that requests the change has to be informed of this 
additional change with a push as any other session. Basically, 
item-from and item-to are linked, they always are the same value.


This makes a  kind of restricted XMPP protocol. It's not great, but 
it's the only sensible way I found to go on. Has anyone already done 
something approaching ? Is it bad ?


Thanks very much again for all the help you provide :)

--
Sylvain Gugli Guglielmi






--
Sylvain Gugli Guglielmi





Re: Roster module with custom MySQL requests

2014-04-01 Thread Sylvain Guglielmi

Le 01/04/2014 14:23, Tomasz Sterna a écrit :

Just remove 'active' module from 'user-load' chain in your sm.xml.


Oh, haven't thought of that. *dumb me*
 (I wrongly assumed all the modules would fail to user_load without 
calling user_create first. Didn't think it was the purpose of the active 
module and that all the others should do fine xD).


I found only one other module relying on the active status : 
roster-publish. I need to deactivate roster-publish (it relies on 
roster-items and roster-group that are not used when using my custom 
roster plugin, and i don't use ldap).


Is it safe/better/not a good idea to deactivate the active plugin from 
every chain (user_load; user_create; user_delete) ?


The sm.xml seems to indicate that the deliver plugin also use the active 
status, but I couldn't find any of this in the code.





You may want to implement the fail-if-not-exist function in your own
roster module in this chain.

For now, I think it's ok : users can not connect to any c2s without 
existing and can not register through XMPP. But I take good note of 
this advice if ever I need it.


Thank you very much for your time.

--
Sylvain Gugli Guglielmi





Re: Roster module with custom MySQL requests

2014-04-01 Thread Tomasz Sterna
Dnia 2014-04-01, wto o godzinie 15:56 +0200, Sylvain Guglielmi pisze:
 Is it safe/better/not a good idea to deactivate the active plugin from
 every chain (user_load; user_create; user_delete) ?

It's main function is to drop messages to unexisting users instead of
storing them in offline messages store.
If you remove it, you are potentially vulnerable to DoS attack filling
your offline storage database with messages for bogus users.


-- 
Tomasz Sterna @ http://abadcafe.pl/ @ http://www.xiaoka.com/





Re: Roster module with custom MySQL requests

2014-04-01 Thread Sylvain Guglielmi

Le 01/04/2014 16:40, Tomasz Sterna a écrit :

Dnia 2014-04-01, wto o godzinie 15:56 +0200, Sylvain Guglielmi pisze:

Is it safe/better/not a good idea to deactivate the active plugin from
every chain (user_load; user_create; user_delete) ?

It's main function is to drop messages to unexisting users instead of
storing them in offline messages store.
If you remove it, you are potentially vulnerable to DoS attack filling
your offline storage database with messages for bogus users.


Ok, good to know. I'll remove it form the user_load chain and let it be 
in the others.


Thanks again.

--
Sylvain Gugli Guglielmi





Re: Roster module with custom MySQL requests

2014-01-17 Thread Sylvain Guglielmi

Le 10/01/2014 18:23, Tomasz Sterna a écrit

You could have a separate cron component pinging 'sm' in regular
intervals with special route packet, and handle this special packet in
'in-router' chain of your module.

Having that it could even be done not in regular intervals, but
on-demand, when your component gets triggered by web frontend.





This works very well ! Thanks for the advice. My cron component is a 
short php script.
I was unsure about how to answer disco#info queries that I get from 
other components, but it seems to work without answering anything. I'll 
read the XEP-0030 : Service Discovery RFC later and try to make a more 
compliant cron component.
If anyone is interested in the php simple DIGEST-MD5 auth, code can be 
found here (It's not robust and exhaustive, but it can be useful I guess) :

https://github.com/Gugli/jabberd2/blob/master/tools/sendupdatepacket.php

I've been scratching my head for the last couple of days on another 
issue : our legacy contact system does not allow asymetrical 
relationships (as XMPP does). It would be very complicated to change 
that to a more XMPP-compliant system. The solution I came up with is 
to make a symmetrical mode for my roster module.

In this mode :
- whenever a user sends a presence subscription, the sm also consider 
the user allows subscription to the same contact.
- whenever a user allows a contact to subscribe to his presence, the sm 
also consider that the user subscribe back to the same contact's presence.
The session that requests the change has to be informed of this 
additional change with a push as any other session. Basically, 
item-from and item-to are linked, they always are the same value.


This makes a  kind of restricted XMPP protocol. It's not great, but 
it's the only sensible way I found to go on. Has anyone already done 
something approaching ? Is it bad ?


Thanks very much again for all the help you provide :)

--
Sylvain Gugli Guglielmi







Re: Roster module with custom MySQL requests

2014-01-10 Thread Sylvain Guglielmi

Another issue I'm facing :

I need my module to be able to detect changes in the database and report 
it to the connected users if necessary. (Use case : the person adds a 
friend with something else than a XMPP client, like a web page interface 
or a webservice API, while she's connected).


The way I choose to do that is :
- the session manager looks for a syncro='out_of_sync' field in the database
- set syncro to 'syncing' for some rows
- then change the memory roster items and push packets to the user's 
sessions with selected changes

- then set syncro to 'ok'.
(In order to make this case simpler, I will do as if there were no row 
deletions, but there are ^_^)


I'm wondering when is the best way to call this function :
- I can make a per-user function, looking only for changes in rows 
concerning a specific user, and call it at the beginning of the 
mod-in_sess and mod-pkt_user functions.
- I can call it on a regular interval (something like 5 seconds) and 
look for changes for any user. Then, if the user is loaded, apply 
changes, and if the user has at least one opened session, send the packets.


For now, I've implemented the first solution. But my feeling is that the 
2nd solution would be better performance-wise (too many requests with 
the 1st). I'm only stopped by the regular interval thing.


My question : *Should I add a timetick chain to the SM (called every 
second for example), and add my module to this chain (with a rate_t 
check) ?* I'm not thrilled by this solution, because for now, I haven't 
changed any code from jabberd2 except from the new module, which make it 
easier to test or get in production. *Is there another, better way ?*


Thanks for reading, and I'm grateful in advance for any useful insight. :)

On a side note : the 5s interval could lead to some race conditions, but 
I think we can avoid any inconststencies (by using INSERT ... ON 
DUPLICATE KEY UPDATE ... instead of UPDATE ... for one). The behavior 
being undetermined if a user issues 2 conflicting commands at the same 
time through differents means is not an issue, if the resulting state is 
the result of one of the user's commands.


--
Sylvain Gugli Guglielmi
Gamedev@Nadeo@Ubisoft



Re: Roster module with custom MySQL requests

2014-01-10 Thread Tomasz Sterna
Dnia 2014-01-10, pią o godzinie 13:54 +0100, Sylvain Guglielmi pisze:
 My question : Should I add a timetick chain to the SM (called every
 second for example), and add my module to this chain (with a rate_t
 check) ? I'm not thrilled by this solution, because for now, I haven't
 changed any code from jabberd2 except from the new module, which make
 it easier to test or get in production. Is there another, better way ?

You could have a separate cron component pinging 'sm' in regular
intervals with special route packet, and handle this special packet in
'in-router' chain of your module.

Having that it could even be done not in regular intervals, but
on-demand, when your component gets triggered by web frontend.





Re: Roster module with custom MySQL requests

2014-01-08 Thread Tomasz Sterna
Dnia 2014-01-08, śro o godzinie 03:02 +0100, Sylvain Gugli Guglielmi
pisze:
 I can have something like UPDATE `roster-items` SET 
 `object-sequence`=`object-sequence`+1 but his break uniqueness in the 
 table.

I come from PostgreSQL, so explicitly handled sequences is natural to
me:
UPDATE roster-items SET object-sequence = nextval('object-sequence')

  I haven't found any easy ways to do something similar with 
 LAST_INSERT_ID() or AUTO_INCREMENT in MySQL, hence my question.

StackOverflow [1] suggests a convoluted solution:
SELECT Auto_increment FROM information_schema.tables WHERE 
table_name='the_table_you_want';

Not pretty.

I think you should be fine with manual increment or
MAX(`object-sequence`)+1


 Just to check : you're talking about the pools in pool.h.

Yes.

 Maybe at one point it'll be better to use pools for roster items too (item, 
 item-name, 
 item-groups and its content). I've not yet profiled anything, but this 
 may be a way to improve speed when loading user data for packet delivery 
 (without having to code 2 load-user events).

I will gladly accept code submission if you decide to do it. :-)



[1] 
http://stackoverflow.com/questions/12271235/mysql-query-next-sequence-number-for-mysql-auto-incremented-field

-- 
Tomasz Sterna @ http://abadcafe.pl/ @ http://www.xiaoka.com/





Re: Roster module with custom MySQL requests

2014-01-07 Thread Tomasz Sterna
Dnia 2014-01-07, wto o godzinie 02:14 +0100, Sylvain Gugli Guglielmi
pisze:
 As I understand it, the 
 object-sequence don't need to be an UNIQUE field. Am I right on
 that ?

object-sequence is used mainly for sorting - to keep stanza ordering,
etc.
There is no enforcement on uniqueness, but in cases when you do care on
ordering, these should be unique.
In roster table you should be fine with just incrementing ver.
BTW, you could just write your UPDATE queries setting ver with next
sequence number for the table. It will save you incrementing in code and
keep it in line with how the original module does things.


 Well, I have broken that clean separation early on. For example I
 needed [...]

Sure. In case of code tied to concrete SQL it's understandable.
I was expressing my concern on changing the generic roster module which
may store data in many formats.

 Also, with many more requests, I fear the jabberd2 load will be more 
 important when I switch our prod to this plugin, and the load is
 already noticeable (15% daily peak of our server)...

My bet is that loading user data for packet delivery causes that.
There is an old item on Rob's TODO list, to have two load-user events -
one when user logs in, and other used for delivery, which loads only
necessary user data. But it is still TODO ;-)


 in my experience mallocs and frees can be long, so I try to minimize 
 these calls as a rule. I assumed the same goes for MySQL requests).

One of the reasons we have memory pools in jabberd2.


-- 
Tomasz Sterna:(){ :|:};:
Instant Messaging ConsultantOpen Source Developer 
http://abadcafe.pl/   http://www.xiaoka.com/portfolio





Re: Roster module with custom MySQL requests

2014-01-07 Thread Sylvain Gugli Guglielmi

Le 07/01/2014 13:22, Tomasz Sterna a écrit :


object-sequence is used mainly for sorting - to keep stanza ordering,
etc.
There is no enforcement on uniqueness, but in cases when you do care on
ordering, these should be unique.
In roster table you should be fine with just incrementing ver.
BTW, you could just write your UPDATE queries setting ver with next
sequence number for the table. It will save you incrementing in code and
keep it in line with how the original module does things.
I can have something like UPDATE `roster-items` SET 
`object-sequence`=`object-sequence`+1 but his break uniqueness in the 
table. I haven't found any easy ways to do something similar with 
LAST_INSERT_ID() or AUTO_INCREMENT in MySQL, hence my question. I think 
I'll stick to that for now.



My bet is that loading user data for packet delivery causes that.
There is an old item on Rob's TODO list, to have two load-user events -
one when user logs in, and other used for delivery, which loads only
necessary user data. But it is still TODO
Ok thanks. If performances really become an issue for me, I may get the 
time to look into that.




One of the reasons we have memory pools in jabberd2.




Just to check : you're talking about the pools in pool.h. For the 
record, group management in the roster do not seem to use them. If I'm 
correct, every time an user changes groups, there are potential 
mallocs/frees. Same goes for _roster_user_load. Maybe at one point it'll 
be better to use pools for roster items too (item, item-name, 
item-groups and its content). I've not yet profiled anything, but this 
may be a way to improve speed when loading user data for packet delivery 
(without having to code 2 load-user events).
Then again, I'll focus on the features I need for now. Perf issues will 
come later.


--
Sylvain Gugli Guglielmi




Re: Roster module with custom MySQL requests

2014-01-06 Thread Tomasz Sterna
Dnia 2014-01-06, pon o godzinie 18:07 +0100, Sylvain Guglielmi pisze:
 Hello everyone,
 
 To use jabberd2 with my pre-existing contacts database, I started
 writing a roster module with customisable MySQL requests (I mailed this
 list a while back about it, but I just started actual work). It uses
 prepared statements, and config file looks like :

This is really interesting. :-D
It could really accelerate XMPP as the next social media protocol. :-)

Could you consider using libdbi instead of hardwiring to MySQL?
This would make your implementation more reusable.


 - In order to make simpler custom databases, I wanted to remove the
 pkt-type == pkt_S10N_UN / item-ask == 2 mechanism. According to a
 comment there is no ask='unsubscribe' in RFC bis anymore . Would
 anyone advise against it ?

How would that make schema simpler?
You still need to store ask='subscribe', don't you?


 - something that seems weird to me : I was expecting item-ver to be
 incremented each time the item is updated (for example in 
 _roster_save_item),
 but couldn't find such code. It there something I don't grasp ?

Ahhh... Yes... There's a little trick I used there. ;-)

ver is coming from auto-incrementing field object-sequence.
As storage never issues UPDATE but always DELETE first then INSERT,
every updated roster item automagically gets new ver value.


 [...] I thought it would be good to do the same
 thing in the regular roster, but I couldn't find a way to remove a
 specific os_object_t. Is there a way to do that ?

storage_delete() with proper filter.


 Anyway here's what the code could look like, with TODOs :

The clean separation between stanza parsing to roster-item and
roster-group structures which are then stored to DB, and in reverse
makes the code more comprehensible.

I doubt that trading readability for efficiency is worthy in case of
today's microprocessor speeds and RAM availability.

Also, does your use-case covers users in many-many groups?
My experience shows that most users are in 0 groups, some in 1 group and
very few in 2+ groups. There's not many INSERTs to gain there.

Remember: Premature optimization is the root of all evil.

I would rather suggest adding 'dirty' flag, risen whenever group
membership actually changes. This would keep the separation still,
allowing for bulk drops of unneeded DELETE/INSERTs.


-- 
Tomasz Sterna:(){ :|:};:
Instant Messaging ConsultantOpen Source Developer 
http://abadcafe.pl/   http://www.xiaoka.com/portfolio





Re: Roster module with custom MySQL requests

2014-01-06 Thread Sylvain Gugli Guglielmi


Le 07/01/2014 00:59, Tomasz Sterna a écrit :

Dnia 2014-01-06, pon o godzinie 18:07 +0100, Sylvain Guglielmi pisze:

Hello everyone,

To use jabberd2 with my pre-existing contacts database, I started
writing a roster module with customisable MySQL requests (I mailed this
list a while back about it, but I just started actual work). It uses
prepared statements, and config file looks like :

This is really interesting. :-D
It could really accelerate XMPP as the next social media protocol. :-)

Could you consider using libdbi instead of hardwiring to MySQL?
This would make your implementation more reusable.


Well, the code is written and I need to go forward right now. But I'll 
look into it when I get the MySQL version running in production. It 
should not be hard provided libdbi works with prepared statements.



- In order to make simpler custom databases, I wanted to remove the
pkt-type == pkt_S10N_UN / item-ask == 2 mechanism. According to a
comment there is no ask='unsubscribe' in RFC bis anymore . Would
anyone advise against it ?

How would that make schema simpler?
You still need to store ask='subscribe', don't you?
Yes, but I assumed it is simpler to explain in the config file the 4th 
parameter of the statement is a boolean meaning that the user is 
requiring subscription instead of a int meaning either requiring a 
subscription or an unsubscription. Especially if it's not a standard 
feature. It also cleans a few lines of code. But you're right : the 
benefit isn't worth bothering. I won't remove it.



- something that seems weird to me : I was expecting item-ver to be
incremented each time the item is updated (for example in
_roster_save_item),
but couldn't find such code. It there something I don't grasp ?

Ahhh... Yes... There's a little trick I used there. ;-)

ver is coming from auto-incrementing field object-sequence.
As storage never issues UPDATE but always DELETE first then INSERT,
every updated roster item automagically gets new ver value.
Ok, I get it now. My custom requests however will use Update... So I'll 
add the increment when the item is modified. As I understand it, the 
object-sequence don't need to be an UNIQUE field. Am I right on that ?





[...] I thought it would be good to do the same
thing in the regular roster, but I couldn't find a way to remove a
specific os_object_t. Is there a way to do that ?

storage_delete() with proper filter.


Thanks for the insight.




Anyway here's what the code could look like, with TODOs :

The clean separation between stanza parsing to roster-item and
roster-group structures which are then stored to DB, and in reverse
makes the code more comprehensible.

I doubt that trading readability for efficiency is worthy in case of
today's microprocessor speeds and RAM availability.
Well, I have broken that clean separation early on. For example I needed 
to check if the max number of contacts in the roster is reached with a 
MySQL request, etc... So this part of readability is already lost in my 
code :S.
Also, with many more requests, I fear the jabberd2 load will be more 
important when I switch our prod to this plugin, and the load is already 
noticeable (15% daily peak of our server)... It seemed to me a good 
tradeoff, I'll stick to that right now. Anyway it'll be easy to revert 
that later on and test the change in performance..



Also, does your use-case covers users in many-many groups?
My experience shows that most users are in 0 groups, some in 1 group and
very few in 2+ groups. There's not many INSERTs to gain there.

Remember: Premature optimization is the root of all evil.
...until your profiling shows 1000 functions each taking 0.1% of the 
time, then you regret not having been careful when writing each of 
them. ^_^ (I lack insight on what is costly for the SessionManager, but 
in my experience mallocs and frees can be long, so I try to minimize 
these calls as a rule. I assumed the same goes for MySQL requests).

I would rather suggest adding 'dirty' flag, risen whenever group
membership actually changes. This would keep the separation still,
allowing for bulk drops of unneeded DELETE/INSERTs.




Thanks for the feedback. I'll keep everyone updated.

--
Sylvain Gugli Guglielmi