From 9181f4f1b1b29588f90eadd70f343c317df91073 Mon Sep 17 00:00:00 2001 From: MithunR Date: Thu, 17 Aug 2023 12:40:23 -0700 Subject: [PATCH 1/3] Translate column size overflow exception to JNI When a CUDF operation causes a column's row count to exceed the size limit imposed by `cudf::size_type`, the operation throws a `std::overflow_error` exception. However, prior to this commit, CUDF JNI did not translate this to a separate Java exception. Because of handling this condition as any generic exception, there was no way to attempt case specific recovery for overflow conditions. This commit translates `std::overflow_error` into a new Java exception (`CudfColumnOverflowException`) that may be caught in user space to attempt recovery/retry. Signed-off-by: MithunR --- .../cudf/CudfColumnOverflowException.java | 34 ++++++++++ java/src/main/native/include/jni_utils.hpp | 5 ++ .../java/ai/rapids/cudf/LargeTableTest.java | 68 +++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100755 java/src/main/java/ai/rapids/cudf/CudfColumnOverflowException.java create mode 100644 java/src/test/java/ai/rapids/cudf/LargeTableTest.java diff --git a/java/src/main/java/ai/rapids/cudf/CudfColumnOverflowException.java b/java/src/main/java/ai/rapids/cudf/CudfColumnOverflowException.java new file mode 100755 index 00000000000..97ec5742baf --- /dev/null +++ b/java/src/main/java/ai/rapids/cudf/CudfColumnOverflowException.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ai.rapids.cudf; + +/** + * Exception thrown when CUDF operation results in a column size + * exceeding CUDF column size limits + */ +public class CudfColumnOverflowException extends CudfException { + CudfColumnOverflowException(String message) { + super(message); + } + + CudfColumnOverflowException(String message, String nativeStacktrace) { + super(message, nativeStacktrace); + } + + CudfColumnOverflowException(String message, String nativeStacktrace, Throwable cause) { + super(message, nativeStacktrace, cause); + } +} diff --git a/java/src/main/native/include/jni_utils.hpp b/java/src/main/native/include/jni_utils.hpp index 07d4c1a9c34..13d8538d9bb 100644 --- a/java/src/main/native/include/jni_utils.hpp +++ b/java/src/main/native/include/jni_utils.hpp @@ -32,6 +32,7 @@ constexpr jint MINIMUM_JNI_VERSION = JNI_VERSION_1_6; constexpr char const *CUDA_ERROR_CLASS = "ai/rapids/cudf/CudaException"; constexpr char const *CUDA_FATAL_ERROR_CLASS = "ai/rapids/cudf/CudaFatalException"; constexpr char const *CUDF_ERROR_CLASS = "ai/rapids/cudf/CudfException"; +constexpr char const *CUDF_OVERFLOW_ERROR_CLASS = "ai/rapids/cudf/CudfColumnOverflowException"; constexpr char const *CUDF_DTYPE_ERROR_CLASS = "ai/rapids/cudf/CudfException"; constexpr char const *INDEX_OOB_CLASS = "java/lang/ArrayIndexOutOfBoundsException"; constexpr char const *ILLEGAL_ARG_CLASS = "java/lang/IllegalArgumentException"; @@ -901,6 +902,10 @@ inline void jni_cuda_check(JNIEnv *const env, cudaError_t cuda_status) { JNI_CHECK_THROW_CUDF_EXCEPTION(env, cudf::jni::CUDF_DTYPE_ERROR_CLASS, e.what(), \ e.stacktrace(), ret_val); \ } \ + catch (std::overflow_error const& e) { \ + JNI_CHECK_THROW_CUDF_EXCEPTION(env, cudf::jni::CUDF_OVERFLOW_ERROR_CLASS, e.what(), \ + "No native stacktrace is available.", ret_val); \ + } \ catch (const std::exception &e) { \ char const *stacktrace = "No native stacktrace is available."; \ if (auto const cudf_ex = dynamic_cast(&e); cudf_ex != nullptr) { \ diff --git a/java/src/test/java/ai/rapids/cudf/LargeTableTest.java b/java/src/test/java/ai/rapids/cudf/LargeTableTest.java new file mode 100644 index 00000000000..b3d22a5ad8e --- /dev/null +++ b/java/src/test/java/ai/rapids/cudf/LargeTableTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019-2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ai.rapids.cudf; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Executable; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Test for operations on tables with large row counts. + */ +public class LargeTableTest extends CudfTestBase { + + static final long RMM_POOL_SIZE_LARGE = 10L * 1024 * 1024 * 1024; + + public LargeTableTest() { + // Set large RMM pool size. Ensure that the test does not run out of memory, + // for large row counts. + super(RmmAllocationMode.POOL, RMM_POOL_SIZE_LARGE); + } + + /** + * Tests that exploding large array columns will result in CudfColumnOverflowException + * if the column size limit is crossed. + */ + @Test + public void testExplodeOverflow() { + int numRows = 1000_000; + int arraySize = 1000; + String str = "abc"; + + // 1 Million rows, each row being { "abc", [ 0, 0, 0... ] }, + // with 1000 elements in the array in each row. + // When the second column is exploded, it produces 1 Billion rows. + // The string row is repeated once for each element in the array, + // thus producing a 1 Billion row string column, with 3 Billion chars + // in the child column. This should cause an overflow exception. + boolean [] arrBools = new boolean[arraySize]; + for (char i = 0; i < arraySize; ++i) { arrBools[i] = false; } + Exception exception = assertThrows(CudfColumnOverflowException.class, ()->{ + try (Scalar strScalar = Scalar.fromString(str); + ColumnVector arrRow = ColumnVector.fromBooleans(arrBools); + Scalar arrScalar = Scalar.listFromColumnView(arrRow); + ColumnVector strVector = ColumnVector.fromScalar(strScalar, numRows); + ColumnVector arrVector = ColumnVector.fromScalar(arrScalar, numRows); + Table inputTable = new Table(strVector, arrVector); + Table outputTable = inputTable.explode(1)) { + assertEquals(outputTable.getColumns()[0].getRowCount(), numRows * arraySize); + fail("Exploding this large table should have caused a CudfColumnOverflowException."); + }}); + assertTrue(exception.getMessage().contains("Size of output exceeds the column size limit")); + } +} \ No newline at end of file From 9ef0c2e15bb41c79fd0f57746620e31e965e790b Mon Sep 17 00:00:00 2001 From: MithunR Date: Thu, 17 Aug 2023 16:04:24 -0700 Subject: [PATCH 2/3] Formatting. --- java/src/main/native/include/jni_utils.hpp | 2 +- java/src/test/java/ai/rapids/cudf/LargeTableTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/java/src/main/native/include/jni_utils.hpp b/java/src/main/native/include/jni_utils.hpp index 13d8538d9bb..3126978ca15 100644 --- a/java/src/main/native/include/jni_utils.hpp +++ b/java/src/main/native/include/jni_utils.hpp @@ -902,7 +902,7 @@ inline void jni_cuda_check(JNIEnv *const env, cudaError_t cuda_status) { JNI_CHECK_THROW_CUDF_EXCEPTION(env, cudf::jni::CUDF_DTYPE_ERROR_CLASS, e.what(), \ e.stacktrace(), ret_val); \ } \ - catch (std::overflow_error const& e) { \ + catch (std::overflow_error const &e) { \ JNI_CHECK_THROW_CUDF_EXCEPTION(env, cudf::jni::CUDF_OVERFLOW_ERROR_CLASS, e.what(), \ "No native stacktrace is available.", ret_val); \ } \ diff --git a/java/src/test/java/ai/rapids/cudf/LargeTableTest.java b/java/src/test/java/ai/rapids/cudf/LargeTableTest.java index b3d22a5ad8e..d209bb03514 100644 --- a/java/src/test/java/ai/rapids/cudf/LargeTableTest.java +++ b/java/src/test/java/ai/rapids/cudf/LargeTableTest.java @@ -65,4 +65,4 @@ public void testExplodeOverflow() { }}); assertTrue(exception.getMessage().contains("Size of output exceeds the column size limit")); } -} \ No newline at end of file +} From b74e96024316798dd9af196862f050ac589497b4 Mon Sep 17 00:00:00 2001 From: MithunR Date: Fri, 18 Aug 2023 11:08:22 -0700 Subject: [PATCH 3/3] Renamed exception to CudfColumnSizeOverflowException. --- ...xception.java => CudfColumnSizeOverflowException.java} | 8 ++++---- java/src/main/native/include/jni_utils.hpp | 2 +- java/src/test/java/ai/rapids/cudf/LargeTableTest.java | 6 ++---- 3 files changed, 7 insertions(+), 9 deletions(-) rename java/src/main/java/ai/rapids/cudf/{CudfColumnOverflowException.java => CudfColumnSizeOverflowException.java} (74%) diff --git a/java/src/main/java/ai/rapids/cudf/CudfColumnOverflowException.java b/java/src/main/java/ai/rapids/cudf/CudfColumnSizeOverflowException.java similarity index 74% rename from java/src/main/java/ai/rapids/cudf/CudfColumnOverflowException.java rename to java/src/main/java/ai/rapids/cudf/CudfColumnSizeOverflowException.java index 97ec5742baf..9e724907a3c 100755 --- a/java/src/main/java/ai/rapids/cudf/CudfColumnOverflowException.java +++ b/java/src/main/java/ai/rapids/cudf/CudfColumnSizeOverflowException.java @@ -19,16 +19,16 @@ * Exception thrown when CUDF operation results in a column size * exceeding CUDF column size limits */ -public class CudfColumnOverflowException extends CudfException { - CudfColumnOverflowException(String message) { +public class CudfColumnSizeOverflowException extends CudfException { + CudfColumnSizeOverflowException(String message) { super(message); } - CudfColumnOverflowException(String message, String nativeStacktrace) { + CudfColumnSizeOverflowException(String message, String nativeStacktrace) { super(message, nativeStacktrace); } - CudfColumnOverflowException(String message, String nativeStacktrace, Throwable cause) { + CudfColumnSizeOverflowException(String message, String nativeStacktrace, Throwable cause) { super(message, nativeStacktrace, cause); } } diff --git a/java/src/main/native/include/jni_utils.hpp b/java/src/main/native/include/jni_utils.hpp index 3126978ca15..ff4da893329 100644 --- a/java/src/main/native/include/jni_utils.hpp +++ b/java/src/main/native/include/jni_utils.hpp @@ -32,7 +32,7 @@ constexpr jint MINIMUM_JNI_VERSION = JNI_VERSION_1_6; constexpr char const *CUDA_ERROR_CLASS = "ai/rapids/cudf/CudaException"; constexpr char const *CUDA_FATAL_ERROR_CLASS = "ai/rapids/cudf/CudaFatalException"; constexpr char const *CUDF_ERROR_CLASS = "ai/rapids/cudf/CudfException"; -constexpr char const *CUDF_OVERFLOW_ERROR_CLASS = "ai/rapids/cudf/CudfColumnOverflowException"; +constexpr char const *CUDF_OVERFLOW_ERROR_CLASS = "ai/rapids/cudf/CudfColumnSizeOverflowException"; constexpr char const *CUDF_DTYPE_ERROR_CLASS = "ai/rapids/cudf/CudfException"; constexpr char const *INDEX_OOB_CLASS = "java/lang/ArrayIndexOutOfBoundsException"; constexpr char const *ILLEGAL_ARG_CLASS = "java/lang/IllegalArgumentException"; diff --git a/java/src/test/java/ai/rapids/cudf/LargeTableTest.java b/java/src/test/java/ai/rapids/cudf/LargeTableTest.java index d209bb03514..d5e0942dfdd 100644 --- a/java/src/test/java/ai/rapids/cudf/LargeTableTest.java +++ b/java/src/test/java/ai/rapids/cudf/LargeTableTest.java @@ -17,8 +17,6 @@ import org.junit.jupiter.api.Test; -import java.lang.reflect.Executable; - import static org.junit.jupiter.api.Assertions.*; /** @@ -52,7 +50,7 @@ public void testExplodeOverflow() { // in the child column. This should cause an overflow exception. boolean [] arrBools = new boolean[arraySize]; for (char i = 0; i < arraySize; ++i) { arrBools[i] = false; } - Exception exception = assertThrows(CudfColumnOverflowException.class, ()->{ + Exception exception = assertThrows(CudfColumnSizeOverflowException.class, ()->{ try (Scalar strScalar = Scalar.fromString(str); ColumnVector arrRow = ColumnVector.fromBooleans(arrBools); Scalar arrScalar = Scalar.listFromColumnView(arrRow); @@ -61,7 +59,7 @@ public void testExplodeOverflow() { Table inputTable = new Table(strVector, arrVector); Table outputTable = inputTable.explode(1)) { assertEquals(outputTable.getColumns()[0].getRowCount(), numRows * arraySize); - fail("Exploding this large table should have caused a CudfColumnOverflowException."); + fail("Exploding this large table should have caused a CudfColumnSizeOverflowException."); }}); assertTrue(exception.getMessage().contains("Size of output exceeds the column size limit")); }