1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 package org.apache.zookeeper.server.quorum;
20 
21 import java.io.PrintWriter;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.TreeMap;
25 import java.util.concurrent.ConcurrentMap;
26 import org.apache.zookeeper.KeeperException.SessionExpiredException;
27 import org.apache.zookeeper.KeeperException.SessionMovedException;
28 import org.apache.zookeeper.KeeperException.UnknownSessionException;
29 import org.apache.zookeeper.server.SessionTrackerImpl;
30 import org.apache.zookeeper.server.ZooKeeperServerListener;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33 
34 /**
35  * The leader session tracker tracks local and global sessions on the leader.
36  */
37 public class LeaderSessionTracker extends UpgradeableSessionTracker {
38 
39     private static final Logger LOG = LoggerFactory.getLogger(LeaderSessionTracker.class);
40 
41     private final SessionTrackerImpl globalSessionTracker;
42 
43     /**
44      * Server id of the leader
45      */
46     private final long serverId;
47 
LeaderSessionTracker(SessionExpirer expirer, ConcurrentMap<Long, Integer> sessionsWithTimeouts, int tickTime, long id, boolean localSessionsEnabled, ZooKeeperServerListener listener)48     public LeaderSessionTracker(SessionExpirer expirer, ConcurrentMap<Long, Integer> sessionsWithTimeouts, int tickTime, long id, boolean localSessionsEnabled, ZooKeeperServerListener listener) {
49 
50         this.globalSessionTracker = new SessionTrackerImpl(expirer, sessionsWithTimeouts, tickTime, id, listener);
51 
52         this.localSessionsEnabled = localSessionsEnabled;
53         if (this.localSessionsEnabled) {
54             createLocalSessionTracker(expirer, tickTime, id, listener);
55         }
56         serverId = id;
57     }
58 
removeSession(long sessionId)59     public void removeSession(long sessionId) {
60         if (localSessionTracker != null) {
61             localSessionTracker.removeSession(sessionId);
62         }
63         globalSessionTracker.removeSession(sessionId);
64     }
65 
start()66     public void start() {
67         globalSessionTracker.start();
68         if (localSessionTracker != null) {
69             localSessionTracker.start();
70         }
71     }
72 
shutdown()73     public void shutdown() {
74         if (localSessionTracker != null) {
75             localSessionTracker.shutdown();
76         }
77         globalSessionTracker.shutdown();
78     }
79 
isGlobalSession(long sessionId)80     public boolean isGlobalSession(long sessionId) {
81         return globalSessionTracker.isTrackingSession(sessionId);
82     }
83 
trackSession(long sessionId, int sessionTimeout)84     public boolean trackSession(long sessionId, int sessionTimeout) {
85         boolean tracked = globalSessionTracker.trackSession(sessionId, sessionTimeout);
86         if (localSessionsEnabled && tracked) {
87             // Only do extra logging so we know what kind of session this is
88             // if we're supporting both kinds of sessions
89             LOG.info("Tracking global session 0x{}", Long.toHexString(sessionId));
90         }
91         return tracked;
92     }
93 
94     /**
95      * Synchronized on this to avoid race condition of adding a local session
96      * after committed global session, which may cause the same session being
97      * tracked on this server and leader.
98      */
commitSession( long sessionId, int sessionTimeout)99     public synchronized boolean commitSession(
100         long sessionId, int sessionTimeout) {
101         boolean added = globalSessionTracker.commitSession(sessionId, sessionTimeout);
102 
103         if (added) {
104             LOG.info("Committing global session 0x{}", Long.toHexString(sessionId));
105         }
106 
107         // If the session moved before the session upgrade finished, it's
108         // possible that the session will be added to the local session
109         // again. Need to double check and remove it from local session
110         // tracker when the global session is quorum committed, otherwise the
111         // local session might be tracked both locally and on leader.
112         //
113         // This cannot totally avoid the local session being upgraded again
114         // because there is still race condition between create another upgrade
115         // request and process the createSession commit, and there is no way
116         // to know there is a on flying createSession request because it might
117         // be upgraded by other server which owns the session before move.
118         if (localSessionsEnabled) {
119             removeLocalSession(sessionId);
120             finishedUpgrading(sessionId);
121         }
122 
123         return added;
124     }
125 
touchSession(long sessionId, int sessionTimeout)126     public boolean touchSession(long sessionId, int sessionTimeout) {
127         if (localSessionTracker != null && localSessionTracker.touchSession(sessionId, sessionTimeout)) {
128             return true;
129         }
130         return globalSessionTracker.touchSession(sessionId, sessionTimeout);
131     }
132 
createSession(int sessionTimeout)133     public long createSession(int sessionTimeout) {
134         if (localSessionsEnabled) {
135             return localSessionTracker.createSession(sessionTimeout);
136         }
137         return globalSessionTracker.createSession(sessionTimeout);
138     }
139 
140     // Returns the serverId from the sessionId (the high order byte)
getServerIdFromSessionId(long sessionId)141     public static long getServerIdFromSessionId(long sessionId) {
142         return sessionId >> 56;
143     }
144 
checkSession(long sessionId, Object owner)145     public void checkSession(long sessionId, Object owner) throws SessionExpiredException, SessionMovedException, UnknownSessionException {
146         if (localSessionTracker != null) {
147             try {
148                 localSessionTracker.checkSession(sessionId, owner);
149                 // A session can both be a local and global session during
150                 // upgrade
151                 if (!isGlobalSession(sessionId)) {
152                     return;
153                 }
154             } catch (UnknownSessionException e) {
155                 // Ignore. We'll check instead whether it's a global session
156             }
157         }
158         try {
159             globalSessionTracker.checkSession(sessionId, owner);
160             // if we can get here, it is a valid global session
161             return;
162         } catch (UnknownSessionException e) {
163             // Ignore. This may be local session from other servers.
164         }
165 
166         /*
167          * if local session is not enabled or it used to be our local session
168          * throw sessions expires
169          */
170         if (!localSessionsEnabled || (getServerIdFromSessionId(sessionId) == serverId)) {
171             throw new SessionExpiredException();
172         }
173     }
174 
checkGlobalSession(long sessionId, Object owner)175     public void checkGlobalSession(long sessionId, Object owner) throws SessionExpiredException, SessionMovedException {
176         try {
177             globalSessionTracker.checkSession(sessionId, owner);
178         } catch (UnknownSessionException e) {
179             // For global session, if we don't know it, it is already expired
180             throw new SessionExpiredException();
181         }
182     }
183 
setOwner(long sessionId, Object owner)184     public void setOwner(long sessionId, Object owner) throws SessionExpiredException {
185         if (localSessionTracker != null) {
186             try {
187                 localSessionTracker.setOwner(sessionId, owner);
188                 return;
189             } catch (SessionExpiredException e) {
190                 // Ignore. We'll check instead whether it's a global session
191             }
192         }
193         globalSessionTracker.setOwner(sessionId, owner);
194     }
195 
dumpSessions(PrintWriter pwriter)196     public void dumpSessions(PrintWriter pwriter) {
197         if (localSessionTracker != null) {
198             pwriter.print("Local ");
199             localSessionTracker.dumpSessions(pwriter);
200             pwriter.print("Global ");
201         }
202         globalSessionTracker.dumpSessions(pwriter);
203     }
204 
setSessionClosing(long sessionId)205     public void setSessionClosing(long sessionId) {
206         // call is no-op if session isn't tracked so safe to call both
207         if (localSessionTracker != null) {
208             localSessionTracker.setSessionClosing(sessionId);
209         }
210         globalSessionTracker.setSessionClosing(sessionId);
211     }
212 
getSessionExpiryMap()213     public Map<Long, Set<Long>> getSessionExpiryMap() {
214         Map<Long, Set<Long>> sessionExpiryMap;
215         // combine local and global sessions, getting local first so upgrades
216         // to global are caught
217         if (localSessionTracker != null) {
218             sessionExpiryMap = localSessionTracker.getSessionExpiryMap();
219         } else {
220             sessionExpiryMap = new TreeMap<Long, Set<Long>>();
221         }
222         sessionExpiryMap.putAll(globalSessionTracker.getSessionExpiryMap());
223         return sessionExpiryMap;
224     }
225 
226 }
227