|
49 | 49 | import com.google.common.base.Supplier;
|
50 | 50 | import com.google.common.collect.ImmutableList;
|
51 | 51 | import com.google.common.collect.ImmutableMap;
|
| 52 | +import com.google.common.collect.ImmutableSet; |
52 | 53 | import com.google.common.util.concurrent.ListenableFuture;
|
53 | 54 | import com.google.common.util.concurrent.MoreExecutors;
|
54 | 55 | import com.google.common.util.concurrent.SettableFuture;
|
@@ -102,6 +103,17 @@ final class SessionPool {
|
102 | 103 | private static final Logger logger = Logger.getLogger(SessionPool.class.getName());
|
103 | 104 | private static final Tracer tracer = Tracing.getTracer();
|
104 | 105 | static final String WAIT_FOR_SESSION = "SessionPool.WaitForSession";
|
| 106 | +static final ImmutableSet<ErrorCode> SHOULD_STOP_PREPARE_SESSIONS_ERROR_CODES = |
| 107 | +ImmutableSet.of( |
| 108 | +ErrorCode.UNKNOWN, |
| 109 | +ErrorCode.INVALID_ARGUMENT, |
| 110 | +ErrorCode.PERMISSION_DENIED, |
| 111 | +ErrorCode.UNAUTHENTICATED, |
| 112 | +ErrorCode.RESOURCE_EXHAUSTED, |
| 113 | +ErrorCode.FAILED_PRECONDITION, |
| 114 | +ErrorCode.OUT_OF_RANGE, |
| 115 | +ErrorCode.UNIMPLEMENTED, |
| 116 | +ErrorCode.INTERNAL); |
105 | 117 |
|
106 | 118 | /**
|
107 | 119 | * Wrapper around current time so that we can fake it in tests. TODO(user): Replace with Java 8
|
@@ -1114,6 +1126,9 @@ private static enum Position {
|
1114 | 1126 | @GuardedBy("lock")
|
1115 | 1127 | private ResourceNotFoundException resourceNotFoundException;
|
1116 | 1128 |
|
| 1129 | +@GuardedBy("lock") |
| 1130 | +private boolean stopAutomaticPrepare; |
| 1131 | + |
1117 | 1132 | @GuardedBy("lock")
|
1118 | 1133 | private final LinkedList<PooledSession> readSessions = new LinkedList<>();
|
1119 | 1134 |
|
@@ -1348,8 +1363,9 @@ private boolean isDatabaseOrInstanceNotFound(SpannerException e) {
|
1348 | 1363 | return e instanceof DatabaseNotFoundException || e instanceof InstanceNotFoundException;
|
1349 | 1364 | }
|
1350 | 1365 |
|
1351 |
| -private boolean isPermissionDenied(SpannerException e) { |
1352 |
| -return e.getErrorCode() == ErrorCode.PERMISSION_DENIED; |
| 1366 | +private boolean shouldStopPrepareSessions(SpannerException e) { |
| 1367 | +return isDatabaseOrInstanceNotFound(e) |
| 1368 | +|| SHOULD_STOP_PREPARE_SESSIONS_ERROR_CODES.contains(e.getErrorCode()); |
1353 | 1369 | }
|
1354 | 1370 |
|
1355 | 1371 | private void invalidateSession(PooledSession session) {
|
@@ -1477,7 +1493,7 @@ PooledSession getReadWriteSession() {
|
1477 | 1493 | // session.
|
1478 | 1494 | while (true) {
|
1479 | 1495 | Waiter waiter = null;
|
1480 |
| -boolean inProcessPrepare = false; |
| 1496 | +boolean inProcessPrepare = stopAutomaticPrepare; |
1481 | 1497 | synchronized (lock) {
|
1482 | 1498 | if (closureFuture != null) {
|
1483 | 1499 | span.addAnnotation("Pool has been closed");
|
@@ -1494,7 +1510,7 @@ PooledSession getReadWriteSession() {
|
1494 | 1510 | }
|
1495 | 1511 | sess = writePreparedSessions.poll();
|
1496 | 1512 | if (sess == null) {
|
1497 |
| -if (numSessionsBeingPrepared <= prepareThreadPoolSize) { |
| 1513 | +if (!inProcessPrepare && numSessionsBeingPrepared <= prepareThreadPoolSize) { |
1498 | 1514 | if (numSessionsBeingPrepared <= readWriteWaiters.size()) {
|
1499 | 1515 | PooledSession readSession = readSessions.poll();
|
1500 | 1516 | if (readSession != null) {
|
@@ -1550,12 +1566,16 @@ PooledSession getReadWriteSession() {
|
1550 | 1566 | if (inProcessPrepare) {
|
1551 | 1567 | try {
|
1552 | 1568 | sess.prepareReadWriteTransaction();
|
| 1569 | +// Session prepare succeeded, restart automatic prepare if it had been stopped. |
| 1570 | +synchronized (lock) { |
| 1571 | +stopAutomaticPrepare = false; |
| 1572 | +} |
1553 | 1573 | } catch (Throwable t) {
|
1554 |
| -sess = null; |
1555 | 1574 | SpannerException e = newSpannerException(t);
|
1556 | 1575 | if (!isClosed()) {
|
1557 | 1576 | handlePrepareSessionFailure(e, sess, false);
|
1558 | 1577 | }
|
| 1578 | +sess = null; |
1559 | 1579 | if (!isSessionNotFound(e)) {
|
1560 | 1580 | throw e;
|
1561 | 1581 | }
|
@@ -1696,25 +1716,30 @@ private void handlePrepareSessionFailure(
|
1696 | 1716 | synchronized (lock) {
|
1697 | 1717 | if (isSessionNotFound(e)) {
|
1698 | 1718 | invalidateSession(session);
|
1699 |
| -} else if (isDatabaseOrInstanceNotFound(e) || isPermissionDenied(e)) { |
1700 |
| -// Database has been deleted or the user has no permission to write to this database. We |
1701 |
| -// should stop trying to prepare any transactions. Also propagate the error to all waiters, |
1702 |
| -// as any further waiting is pointless. |
| 1719 | +} else if (shouldStopPrepareSessions(e)) { |
| 1720 | +// Database has been deleted or the user has no permission to write to this database, or |
| 1721 | +// there is some other semi-permanent error. We should stop trying to prepare any |
| 1722 | +// transactions. Also propagate the error to all waiters if the database or instance has |
| 1723 | +// been deleted, as any further waiting is pointless. |
| 1724 | +stopAutomaticPrepare = true; |
1703 | 1725 | while (readWriteWaiters.size() > 0) {
|
1704 | 1726 | readWriteWaiters.poll().put(e);
|
1705 | 1727 | }
|
1706 | 1728 | while (readWaiters.size() > 0) {
|
1707 | 1729 | readWaiters.poll().put(e);
|
1708 | 1730 | }
|
1709 |
| -// Remove the session from the pool. |
1710 |
| -allSessions.remove(session); |
1711 |
| -if (isClosed()) { |
1712 |
| -decrementPendingClosures(1); |
| 1731 | +if (isDatabaseOrInstanceNotFound(e)) { |
| 1732 | +// Remove the session from the pool. |
| 1733 | +if (isClosed()) { |
| 1734 | +decrementPendingClosures(1); |
| 1735 | +} |
| 1736 | +allSessions.remove(session); |
| 1737 | +this.resourceNotFoundException = |
| 1738 | +MoreObjects.firstNonNull( |
| 1739 | +this.resourceNotFoundException, (ResourceNotFoundException) e); |
| 1740 | +} else { |
| 1741 | +releaseSession(session, Position.FIRST); |
1713 | 1742 | }
|
1714 |
| -this.resourceNotFoundException = |
1715 |
| -MoreObjects.firstNonNull( |
1716 |
| -this.resourceNotFoundException, |
1717 |
| -isDatabaseOrInstanceNotFound(e) ? (ResourceNotFoundException) e : null); |
1718 | 1743 | } else if (informFirstWaiter && readWriteWaiters.size() > 0) {
|
1719 | 1744 | releaseSession(session, Position.FIRST);
|
1720 | 1745 | readWriteWaiters.poll().put(e);
|
@@ -1809,6 +1834,9 @@ private boolean shouldUnblockReader() {
|
1809 | 1834 |
|
1810 | 1835 | private boolean shouldPrepareSession() {
|
1811 | 1836 | synchronized (lock) {
|
| 1837 | +if (stopAutomaticPrepare) { |
| 1838 | +return false; |
| 1839 | +} |
1812 | 1840 | int preparedSessions = writePreparedSessions.size() + numSessionsBeingPrepared;
|
1813 | 1841 | return preparedSessions < Math.floor(options.getWriteSessionsFraction() * totalSessions());
|
1814 | 1842 | }
|
|
0 commit comments