This is the fifth day of my participation in the November Gwen Challenge. Check out the details: The last Gwen Challenge 2021

Introduction to the

After exploring database connection pool fetching in the previous article, let’s take a preliminary look at database connection closing and see what happens

Connection is closed

The specific code below, where relevant, has its own comment:

    @Override
    public void close(a) throws SQLException {
        // Check the following two sections to avoid repeated closing
        if (this.disable) {
            return;
        }

        DruidConnectionHolder holder = this.holder;
        if (holder == null) {
            if (dupCloseLogEnable) {
                LOG.error("dup close");
            }
            return;
        }

        // Check if it is the same thread, if not, use asynchronous closure
        DruidAbstractDataSource dataSource = holder.getDataSource();
        boolean isSameThread = this.getOwnerThread() == Thread.currentThread();

        if(! isSameThread) { dataSource.setAsyncCloseConnectionEnable(true);
        }

        if (dataSource.isAsyncCloseConnectionEnable()) {
            syncClose();
            return;
        }

        if(! CLOSING_UPDATER.compareAndSet(this.0.1)) {
            return;
        }

        try {
            // Some enhancements when connection is closed?
            for (ConnectionEventListener listener : holder.getConnectionEventListeners()) {
                listener.connectionClosed(new ConnectionEvent(this));
            }

            // Filter can also be used when the connection is closed.
            List<Filter> filters = dataSource.getProxyFilters();
            if (filters.size() > 0) {
                FilterChainImpl filterChain = new FilterChainImpl(dataSource);
                filterChain.dataSource_recycle(this);
            } else{ recycle(); }}finally {
            CLOSING_UPDATER.set(this.0);
        }

        this.disable = true;
    }

    
    public void recycle(a) throws SQLException {
        if (this.disable) {
            return;
        }

        DruidConnectionHolder holder = this.holder;
        if (holder == null) {
            if (dupCloseLogEnable) {
                LOG.error("dup close");
            }
            return;
        }

        if (!this.abandoned) {
            DruidAbstractDataSource dataSource = holder.getDataSource();
            dataSource.recycle(this);
        }

        this.holder = null;
        conn = null;
        transactionInfo = null;
        closed = true;
    }
Copy the code

Here is the concrete contents of the recycle() function:

    /** * reclaim connection */
    protected void recycle(DruidPooledConnection pooledConnection) throws SQLException {
        final DruidConnectionHolder holder = pooledConnection.holder;

        if (holder == null) {
            LOG.warn("connectionHolder is null");
            return;
        }

        // A WARN, which does not return directly
        if (logDifferentThread //&& (! isAsyncCloseConnectionEnable())//&& pooledConnection.ownerThread ! = Thread.currentThread()//
        ) {
            LOG.warn("get/close not same thread");
        }

        final Connection physicalConnection = holder.conn;

        // Use the double-checked lock traceEnable
        if (pooledConnection.traceEnable) {
            Object oldInfo = null;
            activeConnectionLock.lock();
            try {
                if (pooledConnection.traceEnable) {
                    oldInfo = activeConnections.remove(pooledConnection);
                    pooledConnection.traceEnable = false; }}finally {
                activeConnectionLock.unlock();
            }
            if (oldInfo == null) {
                if (LOG.isWarnEnabled()) {
                    LOG.warn("remove abandonded failed. activeConnections.size "+ activeConnections.size()); }}}final boolean isAutoCommit = holder.underlyingAutoCommit;
        final boolean isReadOnly = holder.underlyingReadOnly;
        final boolean testOnReturn = this.testOnReturn;

        try {
            // check need to rollback?
            if((! isAutoCommit) && (! isReadOnly)) { pooledConnection.rollback(); }// reset holder, restore default settings, clear warnings
            boolean isSameThread = pooledConnection.ownerThread == Thread.currentThread();
            if(! isSameThread) {final ReentrantLock lock = pooledConnection.lock;
                lock.lock();
                try {
                    holder.reset();
                } finally{ lock.unlock(); }}else {
                holder.reset();
            }

            if (holder.discard) {
                return;
            }

            // If the count identifier is greater than the configured maximum, the connection is closed
            if (phyMaxUseCount > 0 && holder.useCount >= phyMaxUseCount) {
                // The connection is closed
                // At the same time, if the number of active connections is less than the configured minimum number of active connections, a notification will be sent.
                discardConnection(holder);
                return;
            }

            // Count identifier Settings
            if (physicalConnection.isClosed()) {
                lock.lock();
                try {
                    if (holder.active) {
                        activeCount--;
                        holder.active = false;
                    }
                    closeCount++;
                } finally {
                    lock.unlock();
                }
                return;
            }

            // Check whether the connection is still valid, return if not, and set the count identifier bit
            if (testOnReturn) {
                boolean validate = testConnectionInternal(holder, physicalConnection);
                if(! validate) { JdbcUtils.close(physicalConnection); destroyCountUpdater.incrementAndGet(this);

                    lock.lock();
                    try {
                        if (holder.active) {
                            activeCount--;
                            holder.active = false;
                        }
                        closeCount++;
                    } finally {
                        lock.unlock();
                    }
                    return; }}if(holder.initSchema ! =null) {
                holder.conn.setSchema(holder.initSchema);
                holder.initSchema = null;
            }

            // The connection is closed again
            // In the non-enable case
            if(! enable) { discardConnection(holder);return;
            }

            boolean result;
            final long currentTimeMillis = System.currentTimeMillis();

            // Connection timeout closed? What exactly is this timeout
            if (phyTimeoutMillis > 0) {
                long phyConnectTimeMillis = currentTimeMillis - holder.connectTimeMillis;
                if (phyConnectTimeMillis > phyTimeoutMillis) {
                    discardConnection(holder);
                    return; }}// Sets the count flag, and does not close the connection, but puts it into the pool
            lock.lock();
            try {
                if (holder.active) {
                    activeCount--;
                    holder.active = false;
                }
                closeCount++;

                result = putLast(holder, currentTimeMillis);
                recycleCount++;
            } finally {
                lock.unlock();
            }

            if(! result) { JdbcUtils.close(holder.conn); LOG.info("connection recyle failed."); }}catch (Throwable e) {
            // Close the connection if an exception occurs
            holder.clearStatementCache();

            if(! holder.discard) { discardConnection(holder); holder.discard =true;
            }

            LOG.error("recyle error", e);
            recycleErrorCountUpdater.incrementAndGet(this); }}Copy the code

conclusion

As can be seen from the above code:

  • 1.Close is checked for synchronous and asynchronous closure, but there is no significant difference between AsyncClose and RECYCLE. It seems that the connection is not recovered by a newly opened thread, and it also seems to go through synchronization
  • 2. If a certain condition is exceeded, the connection is closed. The rest is put back into the connection pool