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