Hi Maxim, you can't persist on the argumentation that a HashMap stored in the memory is slower then doing a single query for every entry of the map ? Basicaly the previous implementation was a single HashMap. Getting a RoomClient from this map was: map.get($streamId) and now this single line is replaced by a Database query. The performance of this single line is ... I guess smaller then 1 millisecond. How can you say that a "Right now I think the current code should work faster or the same as previous one." :)
I guess you had some other requirements that made you implement this. For example for the clustering solution. However I also persist on my view: A database is _not_ the right place for storing session variables. This is simply a bad design decision that will cost a lot of performance in the future. Could you explain why there is a need for having the RoomClient in the database? Maybe we can resolve those requirements differently so that we can have the RoomClient back in memory. Sebastian 2012/10/19 Maxim Solodovnik <[email protected]> > Sorry for double post. > > Correct me if I'm wrong, but i think "Transient for XML export/import" is > not necessary since it doesn't have @Element annotation > On Oct 19, 2012 9:59 PM, "Maxim Solodovnik" <[email protected]> wrote: > >> RoomClient was mooved to the DB while I implemented cluster. >> Current changes only make the calls faster (since DB is quered only once) >> >> I'll try to create couple of tests. Right now I think the current code >> should work faster or the same as previous one. >> >> Old code did lots of full filterings of RommsClient hash map to get >> subset required. >> >> I tests will show I'm wrong on this I'll try to tune up openjpa cache and >> if it doesn't help will split RoomClient to the couple of objects and/or >> will create hashtable in parallel to speed up things. >> >> But I hope everything is not so slow since these changes are present in >> 2.0 and nobody reports performance degradation. >> >> Sorry for typos, I'm from mobile phone >> On Oct 19, 2012 9:11 PM, "[email protected]" <[email protected]> >> wrote: >> >>> I guess this happened lately in trunk. >>> How are we the in the future planning to determine the current users in >>> the room. >>> Session or database based? >>> >>> I ask that critical as some queries require to sync messages to all >>> clients of a room. >>> This can happen 1000 times in a single second if you think about having >>> up to 1000 concurrent user sessions. >>> >>> For example: >>> ScopeApplicationAdapter::syncMessageToCurrentScope(String >>> remoteMethodName, Object newMessage, boolean sendSelf, boolean sendScreen) >>> => Collection<Set<IConnection>> conCollection = current.getScope() >>> .getConnections(); >>> ==> iterates through all connection of the scope. >>> >>> Now the new thing is that for every connection you will create a query >>> for the RoomClient Object. >>> This is simply impossible. >>> >>> We want to scale up to 1000 concurrent user sessions and more. >>> The RoomClient is a typical Session object and it should stay in the >>> session. >>> It is the same issue like with the WhiteboardObject. >>> We are making a real time application, there is no chance to run queries >>> in sync methods. >>> >>> Can we find some consens here? I know you must have refactored already a >>> lot but I strictly did implement zero db related stuff in sync methods. >>> >>> There might be a chance for some events to trigger db actions.However I >>> don't think having two times the RoomClient stored (one time session, >>> second time database) makes sense. >>> >>> What are you thoughts on this? >>> >>> Sebastian >>> >>> 2012/10/19 [email protected] <[email protected]> >>> >>>> Hi Maxim, >>>> >>>> I don't understand this new code in the Rooms Entity: >>>> @OneToMany(fetch = FetchType.LAZY) >>>> >>>> @JoinColumn(name = "room_id") >>>> private List<RoomClient> currentusers; >>>> >>>> We calc the current users online by iterating through the Red5 >>>> Connections. >>>> In which OpenMeetings version was it changed? >>>> >>>> I guess you can set it Transient for XML export/import. So maybe >>>> annotate it with: >>>> @org.simpleframework.xml.Transient >>>> same like roomOrganisations. >>>> >>>> Sebastian >>>> >>>> 2012/10/19 <[email protected]> >>>> >>>> Author: solomax >>>>> Date: Fri Oct 19 12:39:38 2012 >>>>> New Revision: 1400075 >>>>> >>>>> URL: http://svn.apache.org/viewvc?rev=1400075&view=rev >>>>> Log: >>>>> Wicket: calendar form is improved >>>>> >>>>> Modified: >>>>> >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/data/conference/RoomDAO.java >>>>> >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/persistence/beans/rooms/Rooms.java >>>>> >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/user/calendar/CalendarForm.java >>>>> >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/user/calendar/CalendarPanel.html >>>>> >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/user/calendar/CalendarPanel.java >>>>> >>>>> Modified: >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/data/conference/RoomDAO.java >>>>> URL: >>>>> http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/data/conference/RoomDAO.java?rev=1400075&r1=1400074&r2=1400075&view=diff >>>>> >>>>> ============================================================================== >>>>> --- >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/data/conference/RoomDAO.java >>>>> (original) >>>>> +++ >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/data/conference/RoomDAO.java >>>>> Fri Oct 19 12:39:38 2012 >>>>> @@ -76,6 +76,24 @@ public class RoomDAO implements OmDAO<Ro >>>>> return q.getSingleResult(); >>>>> } >>>>> >>>>> + public List<Rooms> getPublicRooms() { >>>>> + //TypedQuery<Rooms> q = >>>>> em.createNamedQuery("getNondeletedRooms", Rooms.class); >>>>> + TypedQuery<Rooms> q = em.createQuery( >>>>> + "SELECT r from Rooms r LEFT JOIN FETCH >>>>> r.currentusers WHERE r.ispublic= true and r.deleted= false ORDER BY >>>>> r.name ASC", Rooms.class); >>>>> + return q.getResultList(); >>>>> + } >>>>> + >>>>> + public List<Rooms> getOrganisationRooms(long orgId) { >>>>> + TypedQuery<Rooms> q = em.createQuery( >>>>> + "SELECT DISTINCT c.room FROM >>>>> Rooms_Organisation c LEFT JOIN FETCH c.room LEFT JOIN FETCH >>>>> c.room.currentusers " >>>>> + + "WHERE c.organisation.organisation_id = :orgId " >>>>> + + "AND c.deleted = false AND c.room.deleted = false " >>>>> + + "AND c.organisation.deleted = false " >>>>> + + "ORDER BY c.room.name ASC", Rooms.class); >>>>> + q.setParameter("orgId", orgId); >>>>> + return q.getResultList(); >>>>> + } >>>>> + >>>>> public Rooms update(Rooms entity, long userId) { >>>>> if (entity.getRooms_id() == null) { >>>>> /* Red5SIP integration >>>>> *******************************************************************************/ >>>>> >>>>> Modified: >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/persistence/beans/rooms/Rooms.java >>>>> URL: >>>>> http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/persistence/beans/rooms/Rooms.java?rev=1400075&r1=1400074&r2=1400075&view=diff >>>>> >>>>> ============================================================================== >>>>> --- >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/persistence/beans/rooms/Rooms.java >>>>> (original) >>>>> +++ >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/persistence/beans/rooms/Rooms.java >>>>> Fri Oct 19 12:39:38 2012 >>>>> @@ -38,7 +38,6 @@ import javax.persistence.NamedQuery; >>>>> import javax.persistence.OneToMany; >>>>> import javax.persistence.OneToOne; >>>>> import javax.persistence.Table; >>>>> -import javax.persistence.Transient; >>>>> >>>>> import org.apache.openjpa.persistence.ElementDependent; >>>>> import org.apache.openmeetings.persistence.beans.OmEntity; >>>>> @@ -69,7 +68,6 @@ public class Rooms implements Serializab >>>>> private String name; >>>>> >>>>> @Lob >>>>> - // @Basic(fetch=FetchType.LAZY) >>>>> @Column(name = "comment_field") >>>>> @Element(data = true, required = false) >>>>> private String comment; >>>>> @@ -217,10 +215,8 @@ public class Rooms implements Serializab >>>>> @org.simpleframework.xml.Transient >>>>> private List<Rooms_Organisation> roomOrganisations = new >>>>> ArrayList<Rooms_Organisation>(); >>>>> >>>>> - /* >>>>> - * Non persistent attributes >>>>> - */ >>>>> - @Transient >>>>> + @OneToMany(fetch = FetchType.LAZY) >>>>> + @JoinColumn(name = "room_id") >>>>> private List<RoomClient> currentusers; >>>>> >>>>> public String getComment() { >>>>> >>>>> Modified: >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/user/calendar/CalendarForm.java >>>>> URL: >>>>> http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/user/calendar/CalendarForm.java?rev=1400075&r1=1400074&r2=1400075&view=diff >>>>> >>>>> ============================================================================== >>>>> --- >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/user/calendar/CalendarForm.java >>>>> (original) >>>>> +++ >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/user/calendar/CalendarForm.java >>>>> Fri Oct 19 12:39:38 2012 >>>>> @@ -18,14 +18,36 @@ >>>>> */ >>>>> package org.apache.openmeetings.web.components.user.calendar; >>>>> >>>>> +import java.util.ArrayList; >>>>> +import java.util.List; >>>>> + >>>>> +import >>>>> org.apache.openmeetings.data.calendar.daos.AppointmentReminderTypDaoImpl; >>>>> +import org.apache.openmeetings.data.conference.RoomDAO; >>>>> +import org.apache.openmeetings.data.conference.Roommanagement; >>>>> +import org.apache.openmeetings.data.user.dao.UsersDaoImpl; >>>>> import org.apache.openmeetings.persistence.beans.calendar.Appointment; >>>>> +import >>>>> org.apache.openmeetings.persistence.beans.calendar.AppointmentReminderTyps; >>>>> +import >>>>> org.apache.openmeetings.persistence.beans.domain.Organisation_Users; >>>>> +import org.apache.openmeetings.persistence.beans.rooms.RoomTypes; >>>>> +import org.apache.openmeetings.persistence.beans.rooms.Rooms; >>>>> +import org.apache.openmeetings.web.app.Application; >>>>> +import org.apache.openmeetings.web.app.WebSession; >>>>> +import org.apache.wicket.ajax.AjaxRequestTarget; >>>>> +import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox; >>>>> +import org.apache.wicket.extensions.yui.calendar.DateTimeField; >>>>> +import org.apache.wicket.markup.html.form.ChoiceRenderer; >>>>> +import org.apache.wicket.markup.html.form.DropDownChoice; >>>>> import org.apache.wicket.markup.html.form.Form; >>>>> +import org.apache.wicket.markup.html.form.PasswordTextField; >>>>> import org.apache.wicket.markup.html.form.RequiredTextField; >>>>> import org.apache.wicket.markup.html.form.TextArea; >>>>> +import org.apache.wicket.markup.html.form.TextField; >>>>> import org.apache.wicket.model.IModel; >>>>> +import org.apache.wicket.model.PropertyModel; >>>>> >>>>> public class CalendarForm extends Form<Appointment> { >>>>> private static final long serialVersionUID = >>>>> -1764738237821487526L; >>>>> + private boolean createRoom = true; >>>>> >>>>> public CalendarForm(String id, IModel<Appointment> model) { >>>>> super(id, model); >>>>> @@ -33,5 +55,71 @@ public class CalendarForm extends Form<A >>>>> >>>>> add(new RequiredTextField<String>("appointmentName")); >>>>> add(new TextArea<String>("appointmentDescription")); >>>>> + add(new TextField<String>("appointmentLocation")); >>>>> + add(new DateTimeField("appointmentStarttime")); >>>>> + add(new DateTimeField("appointmentEndtime")); >>>>> + final PasswordTextField pwd = new >>>>> PasswordTextField("password"); >>>>> + pwd.setEnabled(isPwdProtected()); >>>>> + pwd.setOutputMarkupId(true); >>>>> + add(pwd); >>>>> + >>>>> + add(new DropDownChoice<AppointmentReminderTyps>( >>>>> + "remind" >>>>> + , >>>>> Application.getBean(AppointmentReminderTypDaoImpl.class).getAppointmentReminderTypList() >>>>> + , new >>>>> ChoiceRenderer<AppointmentReminderTyps>("name", "typId"))); >>>>> + >>>>> + final DropDownChoice<RoomTypes> roomType = new >>>>> DropDownChoice<RoomTypes>( >>>>> + "room.roomtype" >>>>> + , >>>>> Application.getBean(Roommanagement.class).getAllRoomTypes() >>>>> + , new >>>>> ChoiceRenderer<RoomTypes>("name", "roomtypes_id")); >>>>> + roomType.setEnabled(createRoom); >>>>> + roomType.setOutputMarkupId(true); >>>>> + add(roomType); >>>>> + >>>>> + final DropDownChoice<Rooms> room = new >>>>> DropDownChoice<Rooms>( >>>>> + "room" >>>>> + , getRoomList() >>>>> + , new ChoiceRenderer<Rooms>("name", >>>>> "rooms_id")); >>>>> + room.setEnabled(!createRoom); >>>>> + room.setOutputMarkupId(true); >>>>> + add(room); >>>>> + >>>>> + add(new AjaxCheckBox("createRoom", new >>>>> PropertyModel<Boolean>(this, "createRoom")) { >>>>> + private static final long serialVersionUID = >>>>> -3743113990890386035L; >>>>> + >>>>> + @Override >>>>> + protected void onUpdate(AjaxRequestTarget >>>>> target) { >>>>> + createRoom = getConvertedInput(); >>>>> + >>>>> target.add(roomType.setEnabled(createRoom), room.setEnabled(!createRoom)); >>>>> + } >>>>> + }); >>>>> + add(new AjaxCheckBox("isPasswordProtected") { >>>>> + private static final long serialVersionUID = >>>>> 6041200584296439976L; >>>>> + >>>>> + @Override >>>>> + protected void onUpdate(AjaxRequestTarget >>>>> target) { >>>>> + >>>>> CalendarForm.this.getModelObject().setIsPasswordProtected(getConvertedInput()); >>>>> + pwd.setEnabled(isPwdProtected()); >>>>> + target.add(pwd); >>>>> + } >>>>> + }); >>>>> + } >>>>> + >>>>> + private boolean isPwdProtected() { >>>>> + return >>>>> Boolean.TRUE.equals(getModelObject().getIsPasswordProtected()); >>>>> + } >>>>> + >>>>> + private List<Rooms> getRoomList() { >>>>> + //FIXME need to be reviewed >>>>> + List<Rooms> result = new ArrayList<Rooms>(); >>>>> + RoomDAO dao = Application.getBean(RoomDAO.class); >>>>> + result.addAll(dao.getPublicRooms()); >>>>> + for (Organisation_Users ou : >>>>> Application.getBean(UsersDaoImpl.class).get(WebSession.getUserId()).getOrganisation_users()) >>>>> { >>>>> + >>>>> result.addAll(dao.getOrganisationRooms(ou.getOrganisation().getOrganisation_id())); >>>>> + } >>>>> + if (getModelObject().getRoom() != null && >>>>> getModelObject().getRoom().getAppointment()) { //FIXME review >>>>> + result.add(getModelObject().getRoom()); >>>>> + } >>>>> + return result; >>>>> } >>>>> } >>>>> >>>>> Modified: >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/user/calendar/CalendarPanel.html >>>>> URL: >>>>> http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/user/calendar/CalendarPanel.html?rev=1400075&r1=1400074&r2=1400075&view=diff >>>>> >>>>> ============================================================================== >>>>> --- >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/user/calendar/CalendarPanel.html >>>>> (original) >>>>> +++ >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/user/calendar/CalendarPanel.html >>>>> Fri Oct 19 12:39:38 2012 >>>>> @@ -71,7 +71,7 @@ >>>>> $('#appointment').dialog({ >>>>> closeOnEscape: true >>>>> , resizable: true >>>>> - , width: 400 >>>>> + , width: 450 >>>>> , autoOpen: false >>>>> , modal: true >>>>> }); >>>>> @@ -89,53 +89,55 @@ >>>>> <td><wicket:ommessage >>>>> key="572" /></td> >>>>> <td><input type="text" >>>>> wicket:id="appointmentName" /></td> >>>>> </tr> >>>>> - <!-- tr> >>>>> - <td> </td> >>>>> - <td> >>>>> - >>>>> <wicket:ommessage key="570" /><input type="text" >>>>> wicket:id="appointmentStarttime" /> >>>>> - >>>>> <wicket:ommessage key="571" /><input type="text" >>>>> wicket:id="appointmentEndtime" /> >>>>> - </td> >>>>> + <tr> >>>>> + <td><wicket:ommessage >>>>> key="570" /></td> >>>>> + <td><span >>>>> wicket:id="appointmentStarttime"></span></td> >>>>> + </tr> >>>>> + <tr> >>>>> + <td><wicket:ommessage >>>>> key="571" /></td> >>>>> + <td><span >>>>> wicket:id="appointmentEndtime"></span></td> >>>>> </tr> >>>>> <tr> >>>>> <td><wicket:ommessage >>>>> key="565" /></td> >>>>> - <td><select >>>>> wicket:id="notification" ></select></td> >>>>> + <td><select >>>>> wicket:id="remind" ></select></td> >>>>> </tr> >>>>> <tr> >>>>> <td colspan="2"><input >>>>> type="checkbox" wicket:id="createRoom" /><wicket:ommessage key="1509" >>>>> /></td> >>>>> </tr> >>>>> <tr> >>>>> <td><wicket:ommessage >>>>> key="619" /></td> >>>>> - <td><select >>>>> wicket:id="roomType" ></select></td> >>>>> + <td><select >>>>> wicket:id="room.roomtype" ></select></td> >>>>> </tr> >>>>> <tr> >>>>> <td><wicket:ommessage >>>>> key="406" /></td> >>>>> <td><select >>>>> wicket:id="room" ></select></td> >>>>> </tr> >>>>> <tr> >>>>> - <td colspan="2"><input >>>>> type="checkbox" wicket:id="isPasswordProtected" /><wicket:ommessage >>>>> key="1509" /></td> >>>>> + <td colspan="2"><input >>>>> type="checkbox" wicket:id="isPasswordProtected" /><wicket:ommessage >>>>> key="524" /></td> >>>>> </tr> >>>>> <tr> >>>>> <td><wicket:ommessage >>>>> key="525" /></td> >>>>> - <td><input type="text" >>>>> wicket:id="password" /></td> >>>>> + <td><input >>>>> type="password" wicket:id="password" /></td> >>>>> </tr> >>>>> <tr> >>>>> <td >>>>> colspan="2"><wicket:ommessage key="1445" /></td> >>>>> </tr> >>>>> + <!-- >>>>> <tr> >>>>> <td><wicket:ommessage >>>>> key="803" /></td> >>>>> <td><select >>>>> wicket:id="meetingMember" ></select></td> >>>>> - </tr> >>>>> + </tr --> >>>>> <tr> >>>>> <td><wicket:ommessage >>>>> key="569" /></td> >>>>> <td><input type="text" >>>>> wicket:id="appointmentLocation" /></td> >>>>> - </tr--> >>>>> + </tr> >>>>> <tr> >>>>> <td><wicket:ommessage >>>>> key="573" /></td> >>>>> - <td><input type="text" >>>>> wicket:id="appointmentDescription" /></td> >>>>> + <td><textarea >>>>> wicket:id="appointmentDescription"></textarea></td> >>>>> </tr> >>>>> <!-- tr> >>>>> <td colspan="2" >>>>> style="text-align: right"><input type="button" wicket:id="save" /><input >>>>> type="button" wicket:id="cancel" /></td> >>>>> - </tr--> >>>>> + </tr --> >>>>> </table> >>>>> </form> >>>>> </div> >>>>> >>>>> Modified: >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/user/calendar/CalendarPanel.java >>>>> URL: >>>>> http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/user/calendar/CalendarPanel.java?rev=1400075&r1=1400074&r2=1400075&view=diff >>>>> >>>>> ============================================================================== >>>>> --- >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/user/calendar/CalendarPanel.java >>>>> (original) >>>>> +++ >>>>> incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/user/calendar/CalendarPanel.java >>>>> Fri Oct 19 12:39:38 2012 >>>>> @@ -150,6 +150,9 @@ public class CalendarPanel extends UserP >>>>> Calendar end = >>>>> WebSession.getCalendar(); >>>>> >>>>> end.setTimeInMillis(getRequestCycle().getRequest().getRequestParameters().getParameterValue("_end").toLong()); >>>>> >>>>> + if (start.equals(end)) { >>>>> + end.add(Calendar.HOUR_OF_DAY, >>>>> 1); >>>>> + } >>>>> Appointment a = new Appointment(); >>>>> >>>>> a.setAppointmentStarttime(start.getTime()); >>>>> a.setAppointmentEndtime(end.getTime()); >>>>> >>>>> >>>>> >>>> >>>> >>>> -- >>>> Sebastian Wagner >>>> https://twitter.com/#!/dead_lock >>>> http://www.webbase-design.de >>>> http://www.wagner-sebastian.com >>>> [email protected] >>>> >>> >>> >>> >>> -- >>> Sebastian Wagner >>> https://twitter.com/#!/dead_lock >>> http://www.webbase-design.de >>> http://www.wagner-sebastian.com >>> [email protected] >>> >>> >>> -- Sebastian Wagner https://twitter.com/#!/dead_lock http://www.webbase-design.de http://www.wagner-sebastian.com [email protected]
