1 21 22 package org.continuent.sequoia.controller.locks; 23 24 import java.util.ArrayList ; 25 import java.util.Collection ; 26 import java.util.HashMap ; 27 import java.util.HashSet ; 28 import java.util.Iterator ; 29 import java.util.List ; 30 import java.util.Map ; 31 import java.util.Set ; 32 33 import org.continuent.sequoia.common.log.Trace; 34 import org.continuent.sequoia.controller.backend.DatabaseBackend; 35 import org.continuent.sequoia.controller.loadbalancer.BackendTaskQueueEntry; 36 import org.continuent.sequoia.controller.loadbalancer.tasks.AbstractTask; 37 import org.continuent.sequoia.controller.locks.TransactionLogicalLock.WaitingListElement; 38 import org.continuent.sequoia.controller.sql.schema.DatabaseSchema; 39 import org.continuent.sequoia.controller.sql.schema.DatabaseTable; 40 41 51 public class WaitForGraph 52 { 53 private static final String INDENT = " "; 54 private DatabaseBackend backend; 55 private List storedProcedureQueue; 56 private Node victim; 57 58 private static Trace logger = Trace 59 .getLogger("org.continuent.sequoia.controller.loadbalancer"); 60 61 67 public WaitForGraph(DatabaseBackend backend, List storedProcedureQueue) 68 { 69 this.backend = backend; 70 this.storedProcedureQueue = storedProcedureQueue; 71 } 72 73 81 public boolean detectDeadlocks() 82 { 83 Collection nodes = build(); 84 Collection cycles = walk(nodes); 85 kill(cycles); 86 return (victim != null); 87 } 88 89 95 public long getVictimTransactionId() 96 { 97 return victim.getTransactionId(); 98 } 99 100 private Collection build() 101 { 102 if (logger.isDebugEnabled()) 103 logger.debug("Building wait-for graph..."); 104 105 DatabaseSchema schema = backend.getDatabaseSchema(); 107 108 Map transactionToNode = new HashMap (); 109 TransactionLogicalLock globalLock = schema.getLock(); 110 Set uniqueTables = new HashSet (schema.getTables().values()); 115 for (Iterator iter = uniqueTables.iterator(); iter.hasNext();) 116 { 117 DatabaseTable table = (DatabaseTable) iter.next(); 118 TransactionLogicalLock lock = table.getLock(); 119 if (lock.isLocked()) 120 { 121 long lockerTransactionId = lock.getLocker(); 122 if (logger.isDebugEnabled()) 123 logger.debug(table.getName() + " locked by " + lockerTransactionId); 124 Node lockerNode = (Node) transactionToNode.get(new Long ( 125 lockerTransactionId)); 126 if (lockerNode == null) 127 { 128 lockerNode = new Node(lockerTransactionId); 129 transactionToNode.put(new Long (lockerTransactionId), lockerNode); 130 } 131 for (Iterator iter2 = lock.getWaitingList().iterator(); iter2.hasNext();) 132 { 133 long waitingTransactionId = ((WaitingListElement) iter2.next()) 134 .getTransactionId(); 135 Node waitingNode = (Node) transactionToNode.get(new Long ( 136 waitingTransactionId)); 137 if (waitingNode == null) 138 { 139 waitingNode = new Node(waitingTransactionId); 140 transactionToNode.put(new Long (waitingTransactionId), waitingNode); 141 } 142 Edge edge = new Edge(waitingNode, lockerNode, table); 143 waitingNode.addOutgoingEdge(edge); 144 lockerNode.addIncomingEdge(edge); 145 } 146 147 if ((storedProcedureQueue != null) && (globalLock.isLocked())) 149 { 150 for (Iterator iter2 = storedProcedureQueue.iterator(); iter2 151 .hasNext();) 152 { 153 AbstractTask task = ((BackendTaskQueueEntry) iter2.next()) 154 .getTask(); 155 long waitingTransactionId = task.getTransactionId(); 156 if ((waitingTransactionId != lockerTransactionId) 158 && ((task.getLocks(backend) == null) || (task.getLocks(backend) 159 .contains(lock)))) 160 { 161 System.out.println("SP in " + waitingTransactionId 162 + " waiting for " + lockerTransactionId); 163 Node node = (Node) transactionToNode.get(new Long ( 164 waitingTransactionId)); 165 if (node == null) 166 { 167 node = new Node(waitingTransactionId); 168 transactionToNode.put(new Long (waitingTransactionId), node); 169 } 170 Edge edge = new Edge(node, lockerNode, table); 171 node.addOutgoingEdge(edge); 172 lockerNode.addIncomingEdge(edge); 173 } 174 } 175 } 176 } 177 } 178 return transactionToNode.values(); 179 } 180 181 private Collection walk(Collection nodes) 182 { 183 if (logger.isDebugEnabled()) 184 logger.debug("Walking wait-for graph..."); 185 List startNodes = new ArrayList (); 186 List cycles = new ArrayList (); 187 String indent = new String (); 188 189 for (Iterator iter = nodes.iterator(); iter.hasNext();) 191 { 192 Node node = (Node) iter.next(); 193 if (!startNodes.contains(node)) 194 { 195 List visitedNodes = new ArrayList (); 196 doWalk(node, visitedNodes, new Path(), cycles, indent); 197 startNodes.addAll(visitedNodes); 198 } 199 } 200 return cycles; 201 } 202 203 private void doWalk(Node node, List visitedNodes, Path path, List cycles, 204 String indent) 205 { 206 if (path.containsSource(node)) 208 { 209 if (logger.isDebugEnabled()) 210 logger.debug(indent + "Cycle!"); 211 path.trimBeforeSource(node); 212 213 if (!cycles.contains(path)) 215 { 216 cycles.add(path); 217 } 218 219 return; 220 } 221 222 visitedNodes.add(node); 224 for (Iterator iter = node.getOutgoingEdges().iterator(); iter.hasNext();) 225 { 226 Edge edge = (Edge) iter.next(); 227 if (!path.containsEdge(edge)) 228 { 229 Path newPath = new Path(path); 230 newPath.addEdge(edge); 231 if (logger.isDebugEnabled()) 232 logger.debug(indent + node.getTransactionId() + " waits for " 233 + ((DatabaseTable) edge.getResource()).getName() + " locked by " 234 + edge.getSink().getTransactionId()); 235 doWalk(edge.getSink(), visitedNodes, newPath, cycles, indent + INDENT); 236 } 237 } 238 } 239 240 private Node kill(Collection cycles) 241 { 242 if (logger.isDebugEnabled()) 243 logger.debug("Choosing victim node..."); 244 if (logger.isDebugEnabled()) 245 logger.debug(cycles.size() + " cycles detected"); 246 Map appearances = new HashMap (); 247 int maxCount = 0; 248 victim = null; 249 250 for (Iterator iter = cycles.iterator(); iter.hasNext();) 253 { 254 Path cycle = (Path) iter.next(); 255 for (Iterator iter2 = cycle.getSources().iterator(); iter2.hasNext();) 256 { 257 Node node = (Node) iter2.next(); 258 if (appearances.containsKey(node)) 259 { 260 appearances.put(node, new Integer (((Integer ) appearances.get(node)) 261 .intValue() + 1)); 262 } 263 else 264 { 265 appearances.put(node, new Integer (1)); 266 } 267 int value = ((Integer ) appearances.get(node)).intValue(); 268 if (value > maxCount) 269 { 270 maxCount = value; 271 victim = node; 272 } 273 } 274 } 275 276 if (logger.isDebugEnabled()) 277 { 278 for (Iterator iter = cycles.iterator(); iter.hasNext();) 279 { 280 StringBuffer printableCycle = new StringBuffer (INDENT); 281 Path cycle = (Path) iter.next(); 282 Edge edge = null; 283 for (Iterator iter2 = cycle.getEdges().iterator(); iter2.hasNext();) 284 { 285 edge = (Edge) iter2.next(); 286 printableCycle.append(edge.getSource().getTransactionId() + " --" 287 + ((DatabaseTable) edge.getResource()).getName() + "--> "); 288 } 289 printableCycle.append(edge.getSink().getTransactionId()); 290 logger.debug(printableCycle); 291 } 292 if (victim == null) 293 { 294 logger.debug("No victim"); 295 } 296 else 297 { 298 logger.debug("Victim: " + victim.getTransactionId()); 299 } 300 } 301 return victim; 302 } 303 304 private class Node 305 { 306 private long transactionId; 307 private Set outgoingEdges = new HashSet (); 308 private Set incomingEdges = new HashSet (); 309 310 315 public Node(long transactionId) 316 { 317 this.transactionId = transactionId; 318 } 319 320 325 public void addIncomingEdge(Edge edge) 326 { 327 incomingEdges.add(edge); 328 } 329 330 335 public void addOutgoingEdge(Edge edge) 336 { 337 outgoingEdges.add(edge); 338 } 339 340 345 public Set getOutgoingEdges() 346 { 347 return outgoingEdges; 348 } 349 350 355 public long getTransactionId() 356 { 357 return transactionId; 358 } 359 } 360 361 private class Edge 362 { 363 private Object resource; 364 private Node source; 365 private Node sink; 366 367 374 public Edge(Node source, Node sink, Object resource) 375 { 376 this.source = source; 377 this.sink = sink; 378 this.resource = resource; 379 } 380 381 386 public Object getResource() 387 { 388 return resource; 389 } 390 391 396 public Node getSink() 397 { 398 return sink; 399 } 400 401 406 public Node getSource() 407 { 408 return source; 409 } 410 411 } 412 413 private class Path 414 { 415 private List edges; 416 private List sources; 417 private Map sourceToEdge; 418 419 422 public Path() 423 { 424 edges = new ArrayList (); 425 sources = new ArrayList (); 426 sourceToEdge = new HashMap (); 427 } 428 429 434 public Path(Path path) 435 { 436 edges = new ArrayList (path.edges); 437 sources = new ArrayList (path.sources); 438 sourceToEdge = new HashMap (path.sourceToEdge); 439 } 440 441 444 public boolean equals(Object obj) 445 { 446 if (obj == null) 447 { 448 return false; 449 } 450 if (obj instanceof Path) 451 { 452 Set thisEdgesSet = new HashSet (edges); 453 Set objEdgesSet = new HashSet (((Path) obj).edges); 454 return thisEdgesSet.equals(objEdgesSet); 455 } 456 return false; 457 } 458 459 462 public int hashCode() 463 { 464 return (new HashSet (edges)).hashCode(); 465 } 466 467 472 public void addEdge(Edge edge) 473 { 474 edges.add(edge); 475 sources.add(edge.getSource()); 476 sourceToEdge.put(edge.getSource(), edge); 477 } 478 479 485 public boolean containsEdge(Edge edge) 486 { 487 return edges.contains(edge); 488 } 489 490 496 public boolean containsSource(Node node) 497 { 498 return sources.contains(node); 499 } 500 501 506 public List getEdges() 507 { 508 return edges; 509 } 510 511 516 public List getSources() 517 { 518 return sources; 519 } 520 521 526 public void trimBeforeSource(Node node) 527 { 528 edges.subList(0, edges.indexOf(sourceToEdge.get(node))).clear(); 529 sources.subList(0, sources.indexOf(node)).clear(); 530 } 533 } 534 535 } 536 | Popular Tags |