-
Notifications
You must be signed in to change notification settings - Fork 29.3k
[SPARK-24809] [SQL] Serializing LongToUnsafeRowMap in executor may result in data error #21772
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
a72fe61
f67ff4d
06a9547
c9ebfd0
6246dfa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -726,9 +726,10 @@ private[execution] final class LongToUnsafeRowMap(val mm: TaskMemoryManager, cap | |
|
|
||
| writeLong(array.length) | ||
| writeLongArray(writeBuffer, array, array.length) | ||
| val used = ((cursor - Platform.LONG_ARRAY_OFFSET) / 8).toInt | ||
| writeLong(used) | ||
| writeLongArray(writeBuffer, page, used) | ||
|
|
||
| val usedWordsNumber = ((cursor - Platform.LONG_ARRAY_OFFSET) / 8).toInt | ||
| writeLong(usedWordsNumber) | ||
| writeLongArray(writeBuffer, page, usedWordsNumber) | ||
| } | ||
|
|
||
| override def writeExternal(output: ObjectOutput): Unit = { | ||
|
|
@@ -770,8 +771,10 @@ private[execution] final class LongToUnsafeRowMap(val mm: TaskMemoryManager, cap | |
| val length = readLong().toInt | ||
| mask = length - 2 | ||
| array = readLongArray(readBuffer, length) | ||
| val pageLength = readLong().toInt | ||
| page = readLongArray(readBuffer, pageLength) | ||
| val usedWordsNumber = readLong().toInt | ||
| // Set cursor because cursor is used in write function. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe: |
||
| cursor = usedWordsNumber * 8 + Platform.LONG_ARRAY_OFFSET | ||
| page = readLongArray(readBuffer, usedWordsNumber) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto. Can you just update |
||
| } | ||
|
|
||
| override def readExternal(in: ObjectInput): Unit = { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -278,6 +278,39 @@ class HashedRelationSuite extends SparkFunSuite with SharedSQLContext { | |
| map.free() | ||
| } | ||
|
|
||
| test("SPARK-24809: Serializing LongHashedRelation in executor may result in data error") { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to have an end-to-end test for this?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this UT can cover the case I had met. |
||
| val unsafeProj = UnsafeProjection.create(Array[DataType](LongType)) | ||
| val originalMap = new LongToUnsafeRowMap(mm, 1) | ||
|
|
||
| val key1 = 1L | ||
| val value1 = new Random().nextLong() | ||
|
|
||
| val key2 = 2L | ||
| val value2 = new Random().nextLong() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it necessary to use |
||
|
|
||
| originalMap.append(key1, unsafeProj(InternalRow(value1))) | ||
| originalMap.append(key2, unsafeProj(InternalRow(value2))) | ||
| originalMap.optimize() | ||
|
|
||
| val resultRow = new UnsafeRow(1) | ||
| assert(originalMap.getValue(key1, resultRow).getLong(0) === value1) | ||
| assert(originalMap.getValue(key2, resultRow).getLong(0) === value2) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need to test |
||
|
|
||
| val ser = new KryoSerializer( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can write |
||
| (new SparkConf).set("spark.kryo.referenceTracking", "false")).newInstance() | ||
|
|
||
| val mapSerializedInDriver = ser.deserialize[LongToUnsafeRowMap](ser.serialize(originalMap)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: // Simulate serialize/deserialize twice on driver and executor
val firstTimeSerialized = ...
val secondTimeSerialized = ... |
||
| val mapSerializedInExecutor = | ||
| ser.deserialize[LongToUnsafeRowMap](ser.serialize(mapSerializedInDriver)) | ||
|
|
||
| assert(mapSerializedInExecutor.getValue(key1, resultRow).getLong(0) === value1) | ||
| assert(mapSerializedInExecutor.getValue(key2, resultRow).getLong(0) === value2) | ||
|
|
||
| originalMap.free() | ||
| mapSerializedInDriver.free() | ||
| mapSerializedInExecutor.free() | ||
| } | ||
|
|
||
| test("Spark-14521") { | ||
| val ser = new KryoSerializer( | ||
| (new SparkConf).set("spark.kryo.referenceTracking", "false")).newInstance() | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If no good reason, shall we revert this change? Looks like you only rename it?