DBUnit 和 SQL Server 关闭套接字
我在同一位置不断收到 DBUnit 的异常:
org.dbunit.dataset.DataSetException: com.microsoft.sqlserver.jdbc.SQLServerException: Socket closed
at org.dbunit.database.DatabaseTableMetaData.getColumns(DatabaseTableMetaData.java:359)
等。
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
当尝试从表中读取列元数据时会发生这种情况。执行此操作的代码如下所示:
new DefaultTable(tableName,
Columns.getColumns(columns,
connection.createDataSet(new String[]{tableName})
.getTableMetaData(tableName).getColumns()
)
)
连接是一个 MsSqlConnection 实例。起初我以为这是一个网络问题,但这个理论有两个问题。首先运行测试的服务器和数据库都是同一个xen服务器上的虚拟机,所以没有真实的网络。其次,虽然问题不一致,但每次都发生在同一个地方。有超过 100 个数据库测试,但同一个测试失败了(当它失败时)。
有人遇到过类似的问题吗?有什么见解吗?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
经过一些重要的尝试后,还有其他代码测试代码正在读取元数据但没有关闭结果集。现在问题就消失了。
我的理论如下。为了获取 MSSQL 中的数据库元数据,您必须连接到与当前连接不同的数据库。一种方法是更改数据库(MSSQL 中有一个 use 命令)。这种方法的问题在于,您可能会弄乱当前连接的事务,并且如果多个线程访问同一连接,则会引入线程问题。
因此,该解决方案可能会在后台打开一个单独的连接,但如果不是整个虚拟机,则为整个连接共享一个连接对象。 JDBC 仅公开可以关闭的结果集,因此如果您没有对结果集调用 close 并自行关闭它,它们可能会放置一个关闭连接的终结器。问题是,如果其他东西同时读取元数据,它的连接就会从它下面关闭,因此我崩溃了。
鉴于这些测试运行发生在非常一致的代码路径上,内存使用模式肯定有可能相当稳定地运行,导致垃圾收集同时发生,但并不总是在完全相同的时间发生,这符合观察发现它并不总是在完全相同的地方坠毁。
这就是理论。我不确定如何确认,但除非问题再次出现,否则这是我的假设。经验教训:始终关闭读取元数据(以及一般情况)的结果集。
编辑(很长一段时间后):虽然一般来说上述情况可能仍然是正确的,但代码中还存在另一个问题 - 它本身使用了终结器。因此,您有一个连接的包装器,它在终结器中关闭连接,但让连接暴露给其他人。另一个重要的编码规则:如果您的终结器关闭资源,请始终确保在没有引用包含它们的类的情况下没有任何东西可以访问这些资源。
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.