siying commented on code in PR #48355:
URL: https://github.com/apache/spark/pull/48355#discussion_r1866649731
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/streaming/state/RocksDB.scala:
##########
@@ -359,6 +470,56 @@ class RocksDB(
this
}
+ private def init(
+ version: Long,
+ latestSnapshotVersion: Long,
+ metadata: RocksDBCheckpointMetadata): Unit = {
+ loadedVersion = latestSnapshotVersion
+
+ // reset the last snapshot version to the latest available snapshot version
+ lastSnapshotVersion = latestSnapshotVersion
+
+ // Initialize maxVersion upon successful load from DFS
+ fileManager.setMaxSeenVersion(version)
Review Comment:
I understand that the three lines above might be shared between V1 and V2?
But it is confusing to put it here. Perhaps move them back to LoadV1() and
LoadV2().
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/streaming/state/RocksDB.scala:
##########
@@ -431,16 +592,27 @@ class RocksDB(
/**
* Replay change log from the loaded version to the target version.
*/
- private def replayChangelog(endVersion: Long): Unit = {
+ private def replayChangelog(
+ endVersion: Long,
+ stateStoreCkptIdLineage: Option[Array[LineageItem]] = None): Unit = {
+
+ val versionsAndUniqueIds = stateStoreCkptIdLineage match {
+ // First entry of lineage corresponds to loadedVersion
+ case Some(lineage) => lineage.map(i => (i.version,
Some(i.checkpointUniqueId)))
+ case None => (loadedVersion + 1 to endVersion).map((_, None)).toArray
+ }
+
Review Comment:
I feel that the part generating `versionsAndUniqueIds` should be moved
outside `replayChangelog()`, and only `versionsAndUniqueIds` is passed in.
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/streaming/state/RocksDB.scala:
##########
@@ -278,77 +280,186 @@ class RocksDB(
// We send snapshots that needs to be uploaded by the maintenance thread to
this queue
private val snapshotsToUploadQueue = new
ConcurrentLinkedQueue[RocksDBSnapshot]()
+ /**
+ * Based on the ground truth lineage loaded from changelog file (lineage) and
+ * the latest snapshot (version, uniqueId) pair from file listing, this
function finds
+ * the ground truth latest snapshot (version, uniqueId) the db instance
needs to load.
+ *
+ * @param lineage the ground truth lineage loaded from changelog file
+ * @param latestSnapshotVersionsAndUniqueIds
+ * (version, uniqueId) pair of the latest snapshot version. In case of
task re-execution,
+ * the array could have more than one element.
+ * @return the ground truth latest snapshot (version, uniqueId) the db
instance needs to load
+ */
+ private def getLatestSnapshotVersionAndUniqueIdFromLineage(
+ lineage: Array[LineageItem],
+ latestSnapshotVersionsAndUniqueIds: Array[(Long, Option[String])]):
+ (Long, Option[String]) = {
+ lineage.foreach {
+ case LineageItem(version, uniqueId) =>
+ if (latestSnapshotVersionsAndUniqueIds.contains((version,
Some(uniqueId)))) {
+ return (version, Some(uniqueId))
+ }
+ }
+ throw QueryExecutionErrors.cannotFindBaseSnapshotCheckpoint(
+ printLineageItems(lineage),
printLineage(latestSnapshotVersionsAndUniqueIds)
+ )
+ }
+
+ /**
+ * Read the lineage from the changelog files. It first get the changelog
reader
+ * of the correct changelog version and then read the lineage information
from the file.
+ * The changelog file is named as version_stateStoreCkptId.changelog
+ * @param version version of the changelog file, used to load changelog file.
+ * @param stateStoreCkptId uniqueId of the changelog file, used to load
changelog file.
+ * @return
+ */
+ private def getLineageFromChangelogFile(
+ version: Long,
+ stateStoreCkptId: Option[String]): Array[LineageItem] = {
+ var changelogReader: StateStoreChangelogReader = null
+ var currLineage: Array[LineageItem] = Array.empty
+ try {
+ changelogReader = fileManager.getChangelogReader(version,
stateStoreCkptId)
+ currLineage = changelogReader.lineage
+ logInfo(log"Loading lineage: " +
+ log"${MDC(LogKeys.LINEAGE, lineageManager)} from " +
+ log"changelog version: ${MDC(LogKeys.VERSION_NUM, version)} " +
+ log"uniqueId: ${MDC(LogKeys.UUID, stateStoreCkptId.getOrElse(""))}.")
+ } finally {
+ if (changelogReader != null) {
+ changelogReader.closeIfNeeded()
+ }
+ }
+ currLineage
+ }
+
+
/**
* Load the given version of data in a native RocksDB instance.
* Note that this will copy all the necessary file from DFS to local disk as
needed,
* and possibly restart the native RocksDB instance.
*/
- def load(
+ private def loadV2(
+ version: Long,
+ stateStoreCkptId: Option[String],
+ readOnly: Boolean = false): RocksDB = {
+ // An array contains lineage information from [snapShotVersion, version]
(inclusive both end)
+ var currVersionLineage: Array[LineageItem] = lineageManager.getLineage()
+ try {
+ if (loadedVersion != version || (loadedStateStoreCkptId.isEmpty ||
+ stateStoreCkptId.get != loadedStateStoreCkptId.get)) {
+ closeDB(ignoreException = false)
+
+ val (latestSnapshotVersion, latestSnapshotUniqueId) = {
+ // Special handling when version is 0.
+ // When loading the very first version (0), stateStoreCkptId does not
need to be defined
+ // because there won't be 0.changelog / 0.zip file created in RocksDB
under v2.
+ if (version == 0) {
+ assert(stateStoreCkptId.isEmpty, "stateStoreCkptId should be empty
when version is zero")
+ (0L, None)
+ // When there is a snapshot file, it is the ground truth, we can skip
+ // reconstructing the lineage from changelog file.
+ } else if (fileManager.existsSnapshotFile(version, stateStoreCkptId)) {
+ currVersionLineage = Array(LineageItem(version,
stateStoreCkptId.get))
+ lineageManager.resetLineage(currVersionLineage)
+ (version, stateStoreCkptId)
+ } else {
+ val latestSnapshotVersionsAndUniqueIds =
+ fileManager.getLatestSnapshotVersionAndUniqueId(version)
+ currVersionLineage = getLineageFromChangelogFile(version,
stateStoreCkptId) :+
+ LineageItem(version, stateStoreCkptId.get)
+ lineageManager.resetLineage(currVersionLineage)
+ if (latestSnapshotVersionsAndUniqueIds.length == 0) {
+ (0L, None)
+ } else {
+ logInfo(log"Latest snapshot version and uniqueId found: " +
+ log"${MDC(LogKeys.LINEAGE,
printLineage(latestSnapshotVersionsAndUniqueIds))}.")
+ getLatestSnapshotVersionAndUniqueIdFromLineage(
+ currVersionLineage, latestSnapshotVersionsAndUniqueIds)
+ }
+ }
+ }
+
+ logInfo(log"Loaded latestSnapshotVersion: ${
+ MDC(LogKeys.SNAPSHOT_VERSION, latestSnapshotVersion)},
latestSnapshotUniqueId: ${
+ MDC(LogKeys.UUID, latestSnapshotUniqueId)}")
+
+ val metadata = fileManager.loadCheckpointFromDfs(latestSnapshotVersion,
+ workingDir, rocksDBFileMapping, latestSnapshotUniqueId)
+
+ init(version, latestSnapshotVersion, metadata)
+
+ if (loadedVersion != version) {
+ replayChangelog(version, Some(currVersionLineage))
Review Comment:
I hope we make the new argument of `replayChangelog()` more consistent.
`replayChangelog()` currently loads from `loadedVersion` to `endVersion`, and
load delta files of checkpointID from `currVersionLineage`. However,
`currVersionLineage` can have more entries older than `loadedVersion`, and
might have fewer entries, if we aren't able to find snapshot or other problems.
It will be better if we clean this up before calling `replayChangelog()`, and
if the lineage shows up, uses the versions shown in the cleaned lineage.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]