49 private const STATS_LABEL_ANON =
'anonymous';
50 private const STATS_LABEL_AUTH =
'authenticated';
53 private BagOStuff $authenticatedSessionStore;
55 private bool $sameBackend;
62 private LoggerInterface $logger;
67 LoggerInterface $logger,
72 $this->logger->debug(
'SessionManager using anon store ' . get_class( $anonSessionStore ) );
73 $this->logger->debug(
'SessionManager using auth store ' . get_class( $authenticatedSessionStore ) );
75 $this->sameBackend = $anonSessionStore === $authenticatedSessionStore;
76 $this->anonSessionStore = $this->wrapWithCachedBagOStuff( $anonSessionStore );
77 $this->authenticatedSessionStore = $this->wrapWithCachedBagOStuff( $authenticatedSessionStore );
78 $this->statsFactory = $statsFactory;
82 public function setLogger( LoggerInterface $logger ): void {
83 $this->logger = $logger;
91 return new CachedBagOStuff( $store );
102 private function getActiveStore( SessionInfo $sessionInfo ): array {
103 $userInfo = $sessionInfo->getUserInfo();
106 if ( $userInfo ===
null ) {
107 $this->logger->debug(
'No user info found for this session', [
108 'sessionInfo' => (
string)$sessionInfo,
109 'exception' =>
new RuntimeException(),
114 $anonKey = $this->anonSessionStore->makeKey(
'MWSession', $sessionInfo->getId() );
115 $authKey = $this->authenticatedSessionStore->makeKey(
'MWSession', $sessionInfo->getId() );
117 $authData = $this->authenticatedSessionStore->get( $authKey );
118 if ( !$this->authenticatedSessionStore->wasLastGetCached() ) {
119 $this->statsFactory->getCounter(
'sessionstore_nouserinfo_get_total' )
120 ->setLabel(
'wiki', WikiMap::getCurrentWikiId() )
121 ->setLabel(
'type',
'authenticated' )
122 ->setLabel(
'status', $authData ?
'hit' :
'miss' )
126 $anonData = $this->anonSessionStore->get( $anonKey );
127 if ( !$this->anonSessionStore->wasLastGetCached() ) {
128 $this->statsFactory->getCounter(
'sessionstore_nouserinfo_get_total' )
129 ->setLabel(
'wiki', WikiMap::getCurrentWikiId() )
130 ->setLabel(
'type',
'anonymous' )
131 ->setLabel(
'status', $anonData ?
'hit' :
'miss' )
136 $anonUserName = $anonData[
'metadata'][
'userName'] ??
null;
137 $authUserName = $authData[
'metadata'][
'userName'] ??
null;
139 if ( $anonData && $anonUserName !==
null ) {
143 if ( !$this->sameBackend ) {
144 $this->logger->warning(
'No userInfo: authenticated data should not be in the anonymous store', [
145 'sessionInfo' => (
string)$sessionInfo,
146 'exception' =>
new RuntimeException(),
151 $anonUserName =
null;
154 if ( $authData && $authUserName ===
null ) {
155 if ( !$this->sameBackend ) {
156 $this->logger->warning(
'No userInfo: anonymous data should not be in the authenticated store', [
157 'sessionInfo' => (
string)$sessionInfo,
158 'exception' =>
new RuntimeException(),
164 if ( $anonData && $authData && !$this->sameBackend ) {
165 $this->logger->warning(
'Both stores should not have the same session data', [
166 'sessionInfo' => (
string)$sessionInfo,
167 'exception' =>
new RuntimeException(),
172 return [ $this->authenticatedSessionStore, true ];
175 return [ $this->anonSessionStore, false ];
178 if ( !$userInfo->isAnon() ) {
181 return [ $this->authenticatedSessionStore, true ];
184 return [ $this->anonSessionStore, false ];
196 $this->logger->debug( __METHOD__ .
" was called for $info" );
198 [ $store, $isAuthenticated ] = $this->getActiveStore( $info );
200 $key = $store->makeKey(
'MWSession', $info->getId() );
201 $data = $store->get( $key );
202 $userName = $data[
'metadata'][
'userName'] ??
null;
204 if ( $isAuthenticated && $data && $userName ===
null ) {
205 $this->logger->warning(
'Store is authenticated, but the user associated to the data is anonymous', [
206 'sessionInfo' => (
string)$info,
207 'user' => (
string)$info->getUserInfo(),
208 'exception' =>
new RuntimeException(),
212 if ( !$isAuthenticated && $userName !==
null ) {
213 $this->logger->warning(
'Store is anonymous, but the user associated to the data is authenticated', [
214 'sessionInfo' => (
string)$info,
215 'user' => (
string)$info->getUserInfo(),
216 'exception' =>
new RuntimeException(),
222 if ( !$store->wasLastGetCached() ) {
223 $this->statsFactory->getCounter(
'sessionstore_get_total' )
224 ->setLabel(
'type', $isAuthenticated ? self::STATS_LABEL_AUTH : self::STATS_LABEL_ANON )
225 ->setLabel(
'wiki', WikiMap::getCurrentWikiId() )
241 public function set(
SessionInfo $info, $value, $exptime = 0, $flags = 0 ): void {
242 $this->logger->debug( __METHOD__ .
" was called for $info" );
244 if ( $flags === BagOStuff::WRITE_CACHE_ONLY && $value ===
false ) {
251 $anonKey = $this->anonSessionStore->makeKey(
'MWSession', $info->
getId() );
252 $authKey = $this->authenticatedSessionStore->makeKey(
'MWSession', $info->
getId() );
254 $this->anonSessionStore->set( $anonKey,
false, 0, BagOStuff::WRITE_CACHE_ONLY );
255 $this->authenticatedSessionStore->set( $authKey,
false, 0, BagOStuff::WRITE_CACHE_ONLY );
259 [ $store, $isAuthenticated ] = $this->getActiveStore( $info );
261 $key = $store->makeKey(
'MWSession', $info->
getId() );
262 $userName = $value[
'metadata'][
'userName'] ??
null;
268 if ( $isAuthenticated && $userName ===
null ) {
269 $this->logger->warning(
'Store is authenticated, but the user associated to the data is anonymous', [
270 'sessionInfo' => (
string)$info,
271 'user' => (
string)$info->getUserInfo(),
272 'exception' =>
new RuntimeException(),
273 'flags' => (
string)$flags
277 if ( !$isAuthenticated && $userName !==
null ) {
278 $this->logger->warning(
'Store is anonymous, but the user associated to the data is authenticated', [
279 'sessionInfo' => (
string)$info,
280 'user' => (
string)$info->getUserInfo(),
281 'exception' =>
new RuntimeException(),
282 'flags' => (
string)$flags
287 $store->set( $key, $value, $exptime, $flags );
289 if ( ( $flags & BagOStuff::WRITE_CACHE_ONLY ) === 0 ) {
290 $this->statsFactory->getCounter(
'sessionstore_set_total' )
291 ->setLabel(
'type', $isAuthenticated ? self::STATS_LABEL_AUTH : self::STATS_LABEL_ANON )
292 ->
setLabel(
'wiki', WikiMap::getCurrentWikiId() )
303 $this->logger->debug( __METHOD__ .
" was called for $info" );
305 [ $store, $isAuthenticated ] = $this->getActiveStore( $info );
306 $key = $store->makeKey(
'MWSession', $info->getId() );
308 $store->delete( $key );
310 $this->statsFactory->getCounter(
'sessionstore_delete_total' )
311 ->setLabel(
'type', $isAuthenticated ? self::STATS_LABEL_AUTH : self::STATS_LABEL_ANON )
312 ->setLabel(
'wiki',
WikiMap::getCurrentWikiId() )
320 if ( random_int( 1, 100 ) === 1 ) {
321 $this->logger->debug(
'Cleaning session store expired entries' );
323 $this->authenticatedSessionStore->deleteObjectsExpiringBefore( $timeNow );
324 $this->anonSessionStore->deleteObjectsExpiringBefore( $timeNow );