docs: protected void recycle(DruidPooledConnection pooledConnection) throws SQLException {
          final DruidConnectionHolder holder = pooledConnection.getConnectionHolder();
  
          if (holder == null) {
              LOG.warn("connectionHolder is null");
              return;
          }
  
          if (logDifferentThread //
                  && (!isAsyncCloseConnectionEnable()) //
                  && pooledConnection.getOwnerThread() != Thread.currentThread()//
                  ) {
              LOG.warn("get/close not same thread");
          }
  
          final Connection physicalConnection = holder.getConnection();
  
          if (pooledConnection.isTraceEnable()) {
              synchronized (activeConnections) {
                  if (pooledConnection.isTraceEnable()) {
                      Object oldInfo = activeConnections.remove(pooledConnection);
                      if (oldInfo == null) {
                          if (LOG.isWarnEnabled()) {
                              LOG.warn("remove abandonded failed. activeConnections.size " + activeConnections.size());
                          }
                      }
                      pooledConnection.setTraceEnable(false);
                  }
              }
          }
  
          final boolean isAutoCommit = holder.isUnderlyingAutoCommit();
          final boolean isReadOnly = holder.isUnderlyingReadOnly();
          final boolean testOnReturn = this.isTestOnReturn();
  
          try {
              // check need to rollback?
              if ((!isAutoCommit) && (!isReadOnly)) {
                  pooledConnection.rollback();
              }
  
              // reset holder, restore default settings, clear warnings
              boolean isSameThread = pooledConnection.getOwnerThread() == Thread.currentThread();
              if (!isSameThread) {
                  synchronized (pooledConnection) {
                      holder.reset();
                  }
              } else {
                  holder.reset();
              }
  
              if (holder.isDiscard()) {
                  return;
              }
  
              if (testOnReturn) {
                  boolean validate = testConnectionInternal(physicalConnection);
                  if (!validate) {
                      JdbcUtils.close(physicalConnection);
  
                      destroyCount.incrementAndGet();
  
                      lock.lock();
                      try {
                          activeCount--;
                          closeCount++;
                      } finally {
                          lock.unlock();
                      }
                      return;
                  }
              }
  
              if (!enable) {
                  discardConnection(holder.getConnection());
                  return;
              }
  
              final long lastActiveTimeMillis = System.currentTimeMillis();
              lock.lockInterruptibly();
              try {
                  activeCount--;
                  closeCount++;
  
                  putLast(holder, lastActiveTimeMillis);
                  recycleCount++;
              } finally {
                  lock.unlock();
              }
          } catch (Throwable e) {
              holder.clearStatementCache();
  
              if (!holder.isDiscard()) {
                  this.discardConnection(physicalConnection);
                  holder.setDiscard(true);
              }
  
              LOG.error("recyle error", e);
              recycleErrorCount.incrementAndGet();
          }
      }