Wydajny lock per użytkownik w tomcat

1

Cześć wszystkim,

Muszę wrzucać coś do sesji, ale jak użytkownik (per sesja) będzie szybko klikał to wystąpi u mnie typowy race condtion i część danych może się zgubić. Najlepszy lock w takiej sytuacji to lock 'per id sesji'.

Przykładowy serwis:

public class MyService {
 private SessionService sessionService;
 public void doSomething() {
   synchronized ((SessionService.class.getSimpleName() + sessionService.getSessionId()).intern()) {
   sessionService.rememberSomeData(); 
  }
 }
}

(SessionService.class.getSimpleName() + sessionService.getSessionId()) = unikalny lock na dany serwis i sesję użytkownika.
(SessionService.class.getSimpleName() + sessionService.getSessionId()).intern() - gwarantuje, że jest zawsze zwracana ta sama referencja.

Naczytałem się, że nie powinno używać stringów jako locka, pytanie jak inaczej powinienem rozwiązać ten problem? Ilość użytkowników w system jest dosyć duża, czyli liczba locków może być dosyć duża. Sam zapis danych w sesji jest szybką akcją. Ważne, aby użytkownik nie zgubił danych trzymanych sesji.

Pytanie czy da się to zrobić lepiej?

1

Sesja ma atrybuty i jednym z nich może być lock. Spring ma coś takiego jak synchronizeOnSession (RequestMappingHandlerAdapter). Jak się przegląda implementację, to właśnie tak robią:

https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/util/HttpSessionMutexListener.java

public class HttpSessionMutexListener implements HttpSessionListener {

@Override
public void sessionCreated(HttpSessionEvent event) {
	event.getSession().setAttribute(WebUtils.SESSION_MUTEX_ATTRIBUTE, new Mutex());
}

@Override
public void sessionDestroyed(HttpSessionEvent event) {
	event.getSession().removeAttribute(WebUtils.SESSION_MUTEX_ATTRIBUTE);
}


/**
 * The mutex to be registered.
 * Doesn't need to be anything but a plain Object to synchronize on.
 * Should be serializable to allow for HttpSession persistence.
 */
@SuppressWarnings("serial")
private static class Mutex implements Serializable {
}

}

https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/util/WebUtils.java

public static Object getSessionMutex(HttpSession session) {
	Assert.notNull(session, "Session must not be null");
	Object mutex = session.getAttribute(SESSION_MUTEX_ATTRIBUTE);
	if (mutex == null) {
		mutex = session;
	}
	return mutex;
}

Czytanki naprowadzające:
https://stackoverflow.com/questions/11750838/delay-concurrent-requests-from-a-client-until-httpsession-is-created
https://stackoverflow.com/questions/9802165/is-synchronization-within-an-httpsession-feasible

0

@jarekczek: wielkie dzięki! Bardzo ciekawe rozwiązanie, już zacząłem kombinować z pulami locków, ale czułem, że jest to over-engineering. Najgorsze jest to, że jak już się widzi sprawdzone rozwiązanie, to jest ono oczywiste.

1 użytkowników online, w tym zalogowanych: 0, gości: 1