-
Notifications
You must be signed in to change notification settings - Fork 29.3k
[SPARK-34942][API][CORE] Abstract Location in MapStatus to enable support for custom storage #31876
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 1 commit
bd3f5c3
b040a14
1e01dc8
24f20c7
805e1f3
b01252a
eb10171
cb46210
451f963
0286260
66977e0
7ae8116
9bbcd73
2b7833a
9f33047
79d82e5
b010e94
5c0f7ff
e600fc5
701e73c
c3b49db
45b4679
7d411b2
f1d0817
69fb5ed
b6f7a12
02b51bf
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 |
|---|---|---|
|
|
@@ -21,8 +21,7 @@ import java.io.{ObjectInputStream, ObjectOutputStream} | |
|
|
||
| import org.apache.spark.annotation.DeveloperApi | ||
| import org.apache.spark.internal.Logging | ||
| import org.apache.spark.scheduler.AccumulableInfo | ||
| import org.apache.spark.storage.BlockManagerId | ||
| import org.apache.spark.scheduler.{AccumulableInfo, Location} | ||
| import org.apache.spark.util.{AccumulatorV2, Utils} | ||
|
|
||
| // ============================================================================================== | ||
|
|
@@ -81,7 +80,7 @@ case object Resubmitted extends TaskFailedReason { | |
| */ | ||
| @DeveloperApi | ||
| case class FetchFailed( | ||
| bmAddress: BlockManagerId, // Note that bmAddress can be null | ||
| bmAddress: Location, // Note that bmAddress can be null | ||
|
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. This is a backwardly incompatible change, which also impacts event files.
Member
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. For the event files, we still log the json of Besides, do you worry about the possible backward incompatibility due to the reference out of Spark?
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. There are two changes here: a) change to public api (programmatic, REST api, etc), b) change to generated/persisted data. Specifically about (b), with Spark history server, REST consumers, other apps depending on event files - will need a way to identify Location type and serde all valid Location types.
Member
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.
Technically, it's really a problem. Although, I can't imagine how users would use it as an API. And I have a new idea that we can introduce a new fetch failed class for the custom location and leave this one unchanged. For example, we can have
I think we won't change the data of The only problem is the custom location. It's new data, e.g.,
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.
There are couple of issues here:
I actually dont have good solutions on this - other than adding some metadata per location record to indicate the 'type'.
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. +1 for a new class without introducing a breaking change. Thank you for pinging me, @mridulm .
Member
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 we'll have to add interfaces to public JValue serializeToJson();
public Location deserilaizeFromJson(json: JValue);
Adding metadata is good idea, we can have the format like, "Mapstatus Location": {
"type": "xx.yy.zz", // qualified class name
"content": { // content is generate by `Location.serializeToJson`
"aaa":"bbb"
}
}with the constant format, end-users and SHS are able to parse them as well. (I had an idea about SHS is to add the location type as the extension of the event log file. That's the way what compression does now. But I think it doesn't solve the problem of REST case.) BTW, I have added https://issues.apache.org/jira/browse/SPARK-35188 for this support. |
||
| shuffleId: Int, | ||
| mapId: Long, | ||
| mapIndex: Int, | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -28,16 +28,23 @@ import org.apache.spark.internal.config | |||||||||||||||||||||||||||||||||
| import org.apache.spark.storage.BlockManagerId | ||||||||||||||||||||||||||||||||||
| import org.apache.spark.util.Utils | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| trait Location extends Externalizable { | ||||||||||||||||||||||||||||||||||
|
Member
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.
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. Original BlockManagerId extends
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. I might have missed some context here, but why
Member
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. Because the A possible way to let
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. Using
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. To clarify: |
||||||||||||||||||||||||||||||||||
| def host: String | ||||||||||||||||||||||||||||||||||
| def port: Int | ||||||||||||||||||||||||||||||||||
| def hostPort: String | ||||||||||||||||||||||||||||||||||
| def executorId: String = "unknown" | ||||||||||||||||||||||||||||||||||
|
Member
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 added this only for the convenient purpose, doesn't mean I have any preference for the interface. 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. Yeah, adding this here helps for this initial version. Could we add something like
Member
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. For "store extra information", I think implementors can add whatever they want only if they're serializable. e.g., extra info can be But I did think about adding a common 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. Or could we add
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. Based on discussion, if we are going to mirror everything in
Member
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 it (exposing Adding
Member
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.
Do you know other cases that need it? 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. Uber Remote Shuffle Service uses topologyInfo to store shuffle server information. Since we kind of mirror things in
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. I think in |
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||
| * Result returned by a ShuffleMapTask to a scheduler. Includes the block manager address that the | ||||||||||||||||||||||||||||||||||
| * task has shuffle files stored on as well as the sizes of outputs for each reducer, for passing | ||||||||||||||||||||||||||||||||||
| * on to the reduce tasks. | ||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||
| private[spark] sealed trait MapStatus { | ||||||||||||||||||||||||||||||||||
| /** Location where this task output is. */ | ||||||||||||||||||||||||||||||||||
| def location: BlockManagerId | ||||||||||||||||||||||||||||||||||
| def location: Location | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| def updateLocation(newLoc: BlockManagerId): Unit | ||||||||||||||||||||||||||||||||||
| def updateLocation(newLoc: Location): Unit | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||
| * Estimated size for the reduce block, in bytes. | ||||||||||||||||||||||||||||||||||
|
|
@@ -66,7 +73,7 @@ private[spark] object MapStatus { | |||||||||||||||||||||||||||||||||
| .getOrElse(config.SHUFFLE_MIN_NUM_PARTS_TO_HIGHLY_COMPRESS.defaultValue.get) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| def apply( | ||||||||||||||||||||||||||||||||||
| loc: BlockManagerId, | ||||||||||||||||||||||||||||||||||
| loc: Location, | ||||||||||||||||||||||||||||||||||
| uncompressedSizes: Array[Long], | ||||||||||||||||||||||||||||||||||
| mapTaskId: Long): MapStatus = { | ||||||||||||||||||||||||||||||||||
| if (uncompressedSizes.length > minPartitionsToUseHighlyCompressMapStatus) { | ||||||||||||||||||||||||||||||||||
|
|
@@ -115,21 +122,21 @@ private[spark] object MapStatus { | |||||||||||||||||||||||||||||||||
| * @param _mapTaskId unique task id for the task | ||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||
| private[spark] class CompressedMapStatus( | ||||||||||||||||||||||||||||||||||
| private[this] var loc: BlockManagerId, | ||||||||||||||||||||||||||||||||||
| private[this] var loc: Location, | ||||||||||||||||||||||||||||||||||
| private[this] var compressedSizes: Array[Byte], | ||||||||||||||||||||||||||||||||||
| private[this] var _mapTaskId: Long) | ||||||||||||||||||||||||||||||||||
| extends MapStatus with Externalizable { | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // For deserialization only | ||||||||||||||||||||||||||||||||||
| protected def this() = this(null, null.asInstanceOf[Array[Byte]], -1) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| def this(loc: BlockManagerId, uncompressedSizes: Array[Long], mapTaskId: Long) = { | ||||||||||||||||||||||||||||||||||
| def this(loc: Location, uncompressedSizes: Array[Long], mapTaskId: Long) = { | ||||||||||||||||||||||||||||||||||
| this(loc, uncompressedSizes.map(MapStatus.compressSize), mapTaskId) | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| override def location: BlockManagerId = loc | ||||||||||||||||||||||||||||||||||
| override def location: Location = loc | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| override def updateLocation(newLoc: BlockManagerId): Unit = { | ||||||||||||||||||||||||||||||||||
| override def updateLocation(newLoc: Location): Unit = { | ||||||||||||||||||||||||||||||||||
| loc = newLoc | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
@@ -168,7 +175,7 @@ private[spark] class CompressedMapStatus( | |||||||||||||||||||||||||||||||||
| * @param _mapTaskId unique task id for the task | ||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||
| private[spark] class HighlyCompressedMapStatus private ( | ||||||||||||||||||||||||||||||||||
| private[this] var loc: BlockManagerId, | ||||||||||||||||||||||||||||||||||
| private[this] var loc: Location, | ||||||||||||||||||||||||||||||||||
| private[this] var numNonEmptyBlocks: Int, | ||||||||||||||||||||||||||||||||||
| private[this] var emptyBlocks: RoaringBitmap, | ||||||||||||||||||||||||||||||||||
| private[this] var avgSize: Long, | ||||||||||||||||||||||||||||||||||
|
|
@@ -183,9 +190,9 @@ private[spark] class HighlyCompressedMapStatus private ( | |||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| protected def this() = this(null, -1, null, -1, null, -1) // For deserialization only | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| override def location: BlockManagerId = loc | ||||||||||||||||||||||||||||||||||
| override def location: Location = loc | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| override def updateLocation(newLoc: BlockManagerId): Unit = { | ||||||||||||||||||||||||||||||||||
| override def updateLocation(newLoc: Location): Unit = { | ||||||||||||||||||||||||||||||||||
| loc = newLoc | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
@@ -216,7 +223,10 @@ private[spark] class HighlyCompressedMapStatus private ( | |||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| override def readExternal(in: ObjectInput): Unit = Utils.tryOrIOException { | ||||||||||||||||||||||||||||||||||
| loc = BlockManagerId(in) | ||||||||||||||||||||||||||||||||||
| // TODO(wuyi): config | ||||||||||||||||||||||||||||||||||
| val location = "org.apache.spark.storage.BlockManagerId" | ||||||||||||||||||||||||||||||||||
| loc = Utils.classForName(location).newInstance().asInstanceOf[Location] | ||||||||||||||||||||||||||||||||||
| loc.readExternal(in) | ||||||||||||||||||||||||||||||||||
|
Ngone51 marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||||
| numNonEmptyBlocks = -1 // SPARK-32436 Scala 2.13 doesn't initialize this during deserialization | ||||||||||||||||||||||||||||||||||
| emptyBlocks = new RoaringBitmap() | ||||||||||||||||||||||||||||||||||
| emptyBlocks.deserialize(in) | ||||||||||||||||||||||||||||||||||
|
|
@@ -235,7 +245,7 @@ private[spark] class HighlyCompressedMapStatus private ( | |||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| private[spark] object HighlyCompressedMapStatus { | ||||||||||||||||||||||||||||||||||
| def apply( | ||||||||||||||||||||||||||||||||||
| loc: BlockManagerId, | ||||||||||||||||||||||||||||||||||
| loc: Location, | ||||||||||||||||||||||||||||||||||
| uncompressedSizes: Array[Long], | ||||||||||||||||||||||||||||||||||
| mapTaskId: Long): HighlyCompressedMapStatus = { | ||||||||||||||||||||||||||||||||||
| // We must keep track of which blocks are empty so that we don't report a zero-sized | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,12 +17,14 @@ | |
|
|
||
| package org.apache.spark.storage | ||
|
|
||
| import java.io.{Externalizable, IOException, ObjectInput, ObjectOutput} | ||
| import java.io.{IOException, ObjectInput, ObjectOutput} | ||
|
|
||
| import com.google.common.cache.{CacheBuilder, CacheLoader} | ||
|
|
||
| import org.apache.spark.SparkContext | ||
| import org.apache.spark.annotation.DeveloperApi | ||
| import org.apache.spark.scheduler.Location | ||
| import org.apache.spark.storage.BlockManagerId.getCachedBlockManagerId | ||
| import org.apache.spark.util.Utils | ||
|
|
||
| /** | ||
|
|
@@ -40,27 +42,27 @@ class BlockManagerId private ( | |
| private var host_ : String, | ||
| private var port_ : Int, | ||
| private var topologyInfo_ : Option[String]) | ||
| extends Externalizable { | ||
| extends Location { | ||
|
|
||
| private def this() = this(null, null, 0, None) // For deserialization only | ||
|
|
||
| def executorId: String = executorId_ | ||
| override def executorId: String = executorId_ | ||
|
|
||
| if (null != host_) { | ||
| Utils.checkHost(host_) | ||
| assert (port_ > 0) | ||
| } | ||
|
|
||
| def hostPort: String = { | ||
| override def hostPort: String = { | ||
| // DEBUG code | ||
| Utils.checkHost(host) | ||
| assert (port > 0) | ||
| host + ":" + port | ||
| } | ||
|
|
||
| def host: String = host_ | ||
| override def host: String = host_ | ||
|
|
||
| def port: Int = port_ | ||
| override def port: Int = port_ | ||
|
|
||
| def topologyInfo: Option[String] = topologyInfo_ | ||
|
|
||
|
|
@@ -83,6 +85,7 @@ class BlockManagerId private ( | |
| port_ = in.readInt() | ||
| val isTopologyInfoAvailable = in.readBoolean() | ||
| topologyInfo_ = if (isTopologyInfoAvailable) Option(in.readUTF()) else None | ||
| getCachedBlockManagerId(this) | ||
| } | ||
|
|
||
| @throws(classOf[IOException]) | ||
|
|
@@ -129,7 +132,7 @@ private[spark] object BlockManagerId { | |
| def apply(in: ObjectInput): BlockManagerId = { | ||
| val obj = new BlockManagerId() | ||
| obj.readExternal(in) | ||
| getCachedBlockManagerId(obj) | ||
|
Member
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. A reminder for myself: cache is still needed for the use case in |
||
| obj | ||
|
Ngone51 marked this conversation as resolved.
|
||
| } | ||
|
|
||
| /** | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.