views:

392

answers:

1

I keep getting this exception with DBUnit in the same place:

org.dbunit.dataset.DataSetException: com.microsoft.sqlserver.jdbc.SQLServerException: Socket closed
at   org.dbunit.database.DatabaseTableMetaData.getColumns(DatabaseTableMetaData.java:359)

etc.

Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Socket closed
at com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(Unknown Source)
at com.microsoft.sqlserver.jdbc.TDSChannel.read(Unknown Source)
at com.microsoft.sqlserver.jdbc.TDSReader.readPacket(Unknown Source)
at com.microsoft.sqlserver.jdbc.TDSReader.readPacket(Unknown Source)
at com.microsoft.sqlserver.jdbc.TDSCommand.startResponse(Unknown Source)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.doExecutePreparedStatement(Unknown Source)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement$PrepStmtExecCmd.doExecute(Unknown Source)
at com.microsoft.sqlserver.jdbc.TDSCommand.execute(Unknown Source)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(Unknown Source)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeCommand(Unknown Source)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeStatement(Unknown Source)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.executeQueryInternal(Unknown Source)
at com.microsoft.sqlserver.jdbc.SQLServerDatabaseMetaData.getResultSetFromStoredProc(Unknown Source)
at com.microsoft.sqlserver.jdbc.SQLServerDatabaseMetaData.getResultSetWithProvidedColumnNames(Unknown Source)
at com.microsoft.sqlserver.jdbc.SQLServerDatabaseMetaData.getColumns(Unknown Source)
at org.dbunit.database.DefaultMetadataHandler.getColumns(DefaultMetadataHandler.java:52)
at org.dbunit.database.DatabaseTableMetaData.getColumns(DatabaseTableMetaData.java:315)
... 15 more

This happens when trying to read the column metadata from the table. The code doing this looks like this:

 new DefaultTable(tableName,
                    Columns.getColumns(columns,
                            connection.createDataSet(new String[]{tableName})
                                    .getTableMetaData(tableName).getColumns()
                    )
            )

connection being an MsSqlConnection instance. At first I thought it was a network problem, but there are two problems with that theory. First the server running the test and the database are both virtual machines on the same xen server, so there is no real network. Second, although the problem is inconsistent, it happens in the same place every time. There are over 100 database tests, but this same one is the one that fails (when it fails).

Has anyone run across a similar problem? Any insights?

+2  A: 

After some significant playing with this, there was other code test code that was reading metadata but not closing the result set. The problem now went away.

My theory is as follows. In order to get database metadata in MSSQL, you have to connect to a different database than the current connection has. One way to do that is to change databases (there is a use command in MSSQL). The problem with that approach is that you could mess up your transaction with the current connection, and would introduce threading issues if more than one thread accesses the same connection.

So, the solution was likely to open a separate connection under the hood, but sharing one connection object for the overall connection, if not for the whole VM. JDBC only exposes a result set that can be closed, so they may have put a finalizer that closes the connection if you haven't called close on the result set and closed it yourself. The problem with that is that if something else is reading the metadata at the same time, it has its connection closed out from under it, hence my crash.

Given that these test runs were happening on a very consistent code path it is certainly possible that the memory usage pattern was pretty stable run to run, cause garbage collection to happen at the same time, but not always at exactly the same time, which fits the observation that it didn't always crash in exactly the same place.

That is the theory. I'm not sure how to confirm it, but unless the problem comes back, that is my assumption. Lesson learned: Always close the result set from reading the meta data (and in general).

EDIT (after a long time): Although in general the above may still be true, there was another issue going on in the code - it was using a finalizer itself. So you had a wrapper around a connection which was closing the connection in the finalizer but letting the connection be exposed to others. Another important coding rule: if your finalizer closes resources always make sure that nothing can access those resources without having a reference to the class containing them.

Yishai