Skip to content

Commit 25ed577

Browse files
Added a missing unlock in getDestinationMetadata() (#2484)
* Added a missing unlock * Clean up locks as per pr review * Added test * Resolve codeql error * Extend timeout time * Removing test to narrow down on pipeline failures * Adding a clear to see if this fixes prior errors * PR review changes * Address PR comments * More
1 parent 4ccc3a0 commit 25ed577

File tree

2 files changed

+88
-7
lines changed

2 files changed

+88
-7
lines changed

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,10 +1742,10 @@ private void getDestinationMetadata() throws SQLServerException {
17421742
if (null == destColumnMetadata || destColumnMetadata.isEmpty()) {
17431743
if (connection.getcacheBulkCopyMetadata()) {
17441744
DESTINATION_COL_METADATA_LOCK.lock();
1745-
destColumnMetadata = BULK_COPY_OPERATION_CACHE.get(key);
1745+
try {
1746+
destColumnMetadata = BULK_COPY_OPERATION_CACHE.get(key);
17461747

1747-
if (null == destColumnMetadata || destColumnMetadata.isEmpty()) {
1748-
try {
1748+
if (null == destColumnMetadata || destColumnMetadata.isEmpty()) {
17491749
setDestinationColumnMetadata(escapedDestinationTableName);
17501750

17511751
// We are caching the following metadata about the table:
@@ -1759,9 +1759,9 @@ private void getDestinationMetadata() throws SQLServerException {
17591759
// scenario, we can't detect this without making an additional metadata query, which would
17601760
// defeat the purpose of caching.
17611761
BULK_COPY_OPERATION_CACHE.put(key, destColumnMetadata);
1762-
} finally {
1763-
DESTINATION_COL_METADATA_LOCK.unlock();
17641762
}
1763+
} finally {
1764+
DESTINATION_COL_METADATA_LOCK.unlock();
17651765
}
17661766

17671767
if (loggerExternal.isLoggable(Level.FINER)) {

src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
import static org.junit.Assert.assertNotNull;
88
import static org.junit.Assume.assumeTrue;
99
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
10-
import static org.junit.jupiter.api.Assertions.assertTrue;
1110
import static org.junit.jupiter.api.Assertions.assertEquals;
11+
import static org.junit.jupiter.api.Assertions.assertTrue;
1212
import static org.junit.jupiter.api.Assertions.fail;
1313

1414
import java.lang.reflect.Field;
@@ -27,8 +27,12 @@
2727
import java.util.Calendar;
2828
import java.util.HashMap;
2929
import java.util.TimeZone;
30+
import java.util.Timer;
31+
import java.util.TimerTask;
32+
import java.util.concurrent.CountDownLatch;
33+
import java.util.concurrent.ExecutorService;
34+
import java.util.concurrent.Executors;
3035

31-
import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement;
3236
import org.junit.jupiter.api.AfterAll;
3337
import org.junit.jupiter.api.BeforeAll;
3438
import org.junit.jupiter.api.Tag;
@@ -39,6 +43,7 @@
3943

4044
import com.microsoft.sqlserver.jdbc.RandomUtil;
4145
import com.microsoft.sqlserver.jdbc.SQLServerConnection;
46+
import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement;
4247
import com.microsoft.sqlserver.jdbc.SQLServerStatement;
4348
import com.microsoft.sqlserver.jdbc.TestResource;
4449
import com.microsoft.sqlserver.jdbc.TestUtils;
@@ -206,6 +211,82 @@ public void testSqlServerBulkCopyCachingConnectionLevel() throws Exception {
206211
}
207212
}
208213

214+
@Test
215+
public void testSqlServerBulkCopyCachingConnectionLevelMultiThreaded() throws Exception {
216+
// Needs to be on a JDK version greater than 8
217+
assumeTrue(TestUtils.getJVMVersion() > 8);
218+
219+
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
220+
long ms = 1578743412000L;
221+
long timeOut = 30000;
222+
int NUMBER_SIMULTANEOUS_INSERTS = 5;
223+
224+
try (SQLServerConnection con = (SQLServerConnection) DriverManager.getConnection(connectionString
225+
+ ";useBulkCopyForBatchInsert=true;cacheBulkCopyMetadata=true;sendTemporalDataTypesAsStringForBulkCopy=false;");
226+
Statement stmt = con.createStatement()) {
227+
228+
TestUtils.dropTableIfExists(timestampTable1, stmt);
229+
String createSqlTable1 = "CREATE TABLE " + timestampTable1 + " (c1 DATETIME2(3))";
230+
stmt.execute(createSqlTable1);
231+
232+
Field bulkcopyMetadataCacheField;
233+
234+
if (con.getClass().getName().equals("com.microsoft.sqlserver.jdbc.SQLServerConnection43")) {
235+
bulkcopyMetadataCacheField = con.getClass().getSuperclass()
236+
.getDeclaredField("BULK_COPY_OPERATION_CACHE");
237+
} else {
238+
bulkcopyMetadataCacheField = con.getClass().getDeclaredField("BULK_COPY_OPERATION_CACHE");
239+
}
240+
241+
bulkcopyMetadataCacheField.setAccessible(true);
242+
Object bulkcopyCache = bulkcopyMetadataCacheField.get(con);
243+
244+
((HashMap<?, ?>) bulkcopyCache).clear();
245+
246+
TimerTask task = new TimerTask() {
247+
public void run() {
248+
((HashMap<?, ?>) bulkcopyCache).clear();
249+
fail(TestResource.getResource("R_executionTooLong"));
250+
}
251+
};
252+
Timer timer = new Timer("Timer");
253+
timer.schedule(task, timeOut); // Run a timer to help us exit if we get deadlocked
254+
255+
final CountDownLatch countDownLatch = new CountDownLatch(NUMBER_SIMULTANEOUS_INSERTS);
256+
Runnable runnable = () -> {
257+
try {
258+
for (int i = 0; i < 5; ++i) {
259+
PreparedStatement preparedStatement = con
260+
.prepareStatement("INSERT INTO " + timestampTable1 + " VALUES(?)");
261+
Timestamp timestamp = new Timestamp(ms);
262+
preparedStatement.setTimestamp(1, timestamp, gmtCal);
263+
preparedStatement.addBatch();
264+
preparedStatement.executeBatch();
265+
}
266+
countDownLatch.countDown();
267+
countDownLatch.await();
268+
} catch (Exception e) {
269+
fail(TestResource.getResource("R_unexpectedException") + e.getMessage());
270+
} finally {
271+
((HashMap<?, ?>) bulkcopyCache).clear();
272+
}
273+
};
274+
275+
ExecutorService executor = Executors.newFixedThreadPool(NUMBER_SIMULTANEOUS_INSERTS);
276+
277+
try {
278+
for (int i = 0; i < NUMBER_SIMULTANEOUS_INSERTS; i++) {
279+
executor.submit(runnable);
280+
}
281+
executor.shutdown();
282+
} catch (Exception e) {
283+
fail(TestResource.getResource("R_unexpectedException") + e.getMessage());
284+
} finally {
285+
((HashMap<?, ?>) bulkcopyCache).clear();
286+
}
287+
}
288+
}
289+
209290
@Test
210291
public void testValidTimezoneForTimestampBatchInsertWithBulkCopy() throws Exception {
211292
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));

0 commit comments

Comments
 (0)