mirror of
https://github.com/cashapp/zipline.git
synced 2026-03-13 10:23:19 +08:00
Use fresher manifest between cached and embedded (#1789)
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
* Fix: Prevent StackOverflow when serializing recursive class definitions. The serial name
|
||||
description now ends when encountering a recursive descriptor.
|
||||
|
||||
* Fix: Embedded code will now be used first if it is fresher than cached network code.
|
||||
|
||||
## [1.25.0] - 2026-01-12
|
||||
[1.25.0]: https://github.com/cashapp/zipline/releases/tag/1.25.0
|
||||
|
||||
@@ -22,6 +22,7 @@ import app.cash.zipline.loader.internal.fetcher.FsCachingFetcher
|
||||
import app.cash.zipline.loader.internal.fetcher.FsEmbeddedFetcher
|
||||
import app.cash.zipline.loader.internal.fetcher.HttpFetcher
|
||||
import app.cash.zipline.loader.internal.fetcher.LoadedManifest
|
||||
import app.cash.zipline.loader.internal.fetcher.LoadedManifestComparator
|
||||
import app.cash.zipline.loader.internal.fetcher.fetch
|
||||
import app.cash.zipline.loader.internal.getApplicationManifestFileName
|
||||
import app.cash.zipline.loader.internal.receiver.FsSaveReceiver
|
||||
@@ -664,9 +665,15 @@ class ZiplineLoader internal constructor(
|
||||
eventListener: EventListener,
|
||||
nowEpochMs: Long,
|
||||
): LoadedManifest? {
|
||||
val result = cachingFetcher?.loadPinnedManifest(applicationName, nowEpochMs)
|
||||
?: embeddedFetcher?.loadEmbeddedManifest(applicationName)
|
||||
?: return null
|
||||
val pinnedManifest = cachingFetcher?.loadPinnedManifest(applicationName, nowEpochMs)
|
||||
val embeddedManifest = embeddedFetcher?.loadEmbeddedManifest(applicationName)
|
||||
|
||||
// If both are available, pick the freshest of the two
|
||||
val result = if (pinnedManifest != null && embeddedManifest != null) {
|
||||
maxOf(pinnedManifest, embeddedManifest, LoadedManifestComparator)
|
||||
} else {
|
||||
pinnedManifest ?: embeddedManifest ?: return null
|
||||
}
|
||||
|
||||
// Defend against changes to the locally-cached copy.
|
||||
val verifiedKey = manifestVerifier.verify(result.manifestBytes, result.manifest)
|
||||
|
||||
@@ -49,3 +49,12 @@ internal fun LoadedManifest(manifestBytes: ByteString): LoadedManifest {
|
||||
?: error("freshAtEpochMs is required for loaded manifests, but was null")
|
||||
return LoadedManifest(manifestBytes, manifest, freshAtEpochMs)
|
||||
}
|
||||
|
||||
internal object LoadedManifestComparator : Comparator<LoadedManifest> {
|
||||
override fun compare(
|
||||
a: LoadedManifest,
|
||||
b: LoadedManifest,
|
||||
): Int {
|
||||
return a.freshAtEpochMs.compareTo(b.freshAtEpochMs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ class LoaderTester(
|
||||
cache.close()
|
||||
}
|
||||
|
||||
fun seedEmbedded(applicationName: String, seed: String) {
|
||||
fun seedEmbedded(applicationName: String, seed: String, freshAtEpochMs: Long = 5L) {
|
||||
embeddedFileSystem.createDirectories(embeddedDir)
|
||||
val ziplineFileByteString =
|
||||
testFixtures.createZiplineFile(LoaderTestFixtures.createJs(seed), "$seed.js")
|
||||
@@ -102,7 +102,7 @@ class LoaderTester(
|
||||
val embeddedManifest = LoaderTestFixtures.createRelativeEmbeddedManifest(
|
||||
seed = seed,
|
||||
seedFileSha256 = sha256,
|
||||
seedFreshAtEpochMs = 5L,
|
||||
seedFreshAtEpochMs = freshAtEpochMs,
|
||||
includeUnknownFieldInJson = includeUnknownFieldInJson,
|
||||
)
|
||||
embeddedFileSystem.write(embeddedDir / sha256.hex()) {
|
||||
|
||||
@@ -53,6 +53,7 @@ class ZiplineLoaderTest {
|
||||
private lateinit var httpClient: FakeZiplineHttpClient
|
||||
private lateinit var embeddedFileSystem: FileSystem
|
||||
private lateinit var embeddedDir: Path
|
||||
private lateinit var cache: ZiplineCache
|
||||
|
||||
private val testFixtures = LoaderTestFixtures()
|
||||
|
||||
@@ -63,6 +64,7 @@ class ZiplineLoaderTest {
|
||||
httpClient = tester.httpClient
|
||||
embeddedFileSystem = tester.embeddedFileSystem
|
||||
embeddedDir = tester.embeddedDir
|
||||
cache = tester.cache
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
@@ -410,6 +412,32 @@ class ZiplineLoaderTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun freshestCachedOrEmbeddedManifestTakesPrecedence() = runBlocking {
|
||||
cache.pinManifest("test", LoadedManifest(testFixtures.manifestByteString, freshAtEpochMs = 25L), tester.nowMillis)
|
||||
|
||||
// Embedded code is fresher than cached code
|
||||
tester.seedEmbedded("test", "test", freshAtEpochMs = 100L)
|
||||
|
||||
val zipline = (
|
||||
loader.loadOnce(
|
||||
applicationName = "test",
|
||||
freshnessChecker = FakeFreshnessCheckerFresh,
|
||||
manifestUrl = MANIFEST_URL,
|
||||
) as LoadResult.Success
|
||||
).zipline
|
||||
|
||||
// Loaded the embedded "test" code, not the cached "alpha/bravo" code
|
||||
assertEquals(
|
||||
"""
|
||||
|test loaded
|
||||
|
|
||||
""".trimMargin(),
|
||||
zipline.getLog(),
|
||||
)
|
||||
zipline.close()
|
||||
}
|
||||
|
||||
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") // Access :zipline-loader internals.
|
||||
private fun Zipline.getLog(): String? = app.cash.zipline.internal.getLog(quickJs)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user