-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Native-image metadata for all modules (#32305)
* feat: Native-image metadata for all modules * Check in akka-actor metadata * Some more manual ones * Some more pluggable types * Some more stuff * akka-remote * akka-cluster * akka-cluster-metrics * akka-cluster-sharding * akka-cluster-sharding-typed * akka-cluster-tools * akka-cluster-typed * akka-coordination * akka-service-discovery * akka-distributed-data * akka-persistence * akka-persistence-query * akka-persistence-typed * akka-pki * akka-serialization-jackson * akka-slf4j * akka-stream * akka-stream-typed * Unsafe * ClusterActorRefProvider * LocalActorRefProvider * RemoteActorRefProvider * generic search for ClusterConfigChecker * a bit more * Trying out a CI test scheme inspired by akka-projection samples * Most non-cluster features covered in native-image test and working
- Loading branch information
1 parent
899e984
commit 0e6ac9c
Showing
56 changed files
with
3,062 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
name: Native Image Tests | ||
|
||
on: | ||
schedule: | ||
- cron: "0 0 * * *" | ||
workflow_dispatch: | ||
|
||
permissions: | ||
contents: read | ||
|
||
jobs: | ||
native-image-tests: | ||
name: Run Native Image Tests | ||
runs-on: ubuntu-22.04 | ||
steps: | ||
- name: Checkout | ||
# https://github.com/actions/checkout/releases | ||
# v4.1.1 | ||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 | ||
with: | ||
fetch-depth: 0 | ||
|
||
- name: Checkout GitHub merge | ||
if: github.event.pull_request | ||
run: |- | ||
git fetch origin pull/${{ github.event.pull_request.number }}/merge:scratch | ||
git checkout scratch | ||
- name: Cache Coursier cache | ||
# https://github.com/coursier/cache-action/releases | ||
# v6.4.4 | ||
uses: coursier/cache-action@a0e7cd24be81bc84f0d7461e02bd1a96980553d7 | ||
|
||
- name: Set up JDK 11 | ||
# https://github.com/coursier/setup-action/releases | ||
# v1.3.4 | ||
uses: coursier/setup-action@48280172a2c999022e42527711d6b28e4945e6f0 | ||
with: | ||
jvm: temurin:1.11 | ||
|
||
- name: Gather version | ||
run: |- | ||
echo `git describe --tags | sed -e "s/v\(.*\)-\([0-9][0-9]*\).*/\\1-\\2-/"``git rev-parse HEAD | head -c8`-SNAPSHOT > ~/.version | ||
cat ~/.version | ||
- name: Publish artifacts locally | ||
run: |- | ||
sbt "publishLocal; publishM2" | ||
- name: Local drone control sample Scala native image build | ||
run: |- | ||
cd native-image-tests/local-scala | ||
sbt nativeImage -Dakka.version=`cat ~/.version` | ||
# FIXME we need something more, actually running it as well to detect errors |
205 changes: 205 additions & 0 deletions
205
akka-actor-tests/src/test/scala/akka/NativeImageMetadataSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
/* | ||
* Copyright (C) 2009-2023 Lightbend Inc. <https://www.lightbend.com> | ||
*/ | ||
|
||
package akka | ||
|
||
import akka.actor.ActorSystem | ||
import akka.actor.DynamicAccess | ||
import akka.actor.LocalActorRefProvider | ||
import akka.actor.Props | ||
import akka.event.EventStream | ||
import akka.routing.RouterConfig | ||
import akka.testkit.NativeImageUtils | ||
import akka.testkit.NativeImageUtils.Constructor | ||
import akka.testkit.NativeImageUtils.ReflectConfigEntry | ||
import akka.testkit.NativeImageUtils.ReflectField | ||
import akka.testkit.NativeImageUtils.ReflectMethod | ||
import com.typesafe.config.ConfigFactory | ||
import org.scalatest.matchers.should.Matchers | ||
import org.scalatest.wordspec.AnyWordSpec | ||
import akka.util.ccompat.JavaConverters._ | ||
|
||
object NativeImageMetadataSpec { | ||
|
||
val metadataDir = NativeImageUtils.metadataDirFor("akka-actor") | ||
|
||
val additionalEntries = Seq( | ||
// dungeon or dungeon worthy unsafe trixery | ||
// FIXME ugh, maybe we could reflect generate it instead of manually listing? | ||
ReflectConfigEntry( | ||
classOf[sun.misc.Unsafe].getName, | ||
methods = Seq( | ||
ReflectMethod("arrayBaseOff", parameterTypes = Seq("java.lang.Class")), | ||
ReflectMethod("arrayIndexScale", parameterTypes = Seq("java.lang.Class")), | ||
ReflectMethod("copyMemory", parameterTypes = Seq("long", "long", "long")), | ||
ReflectMethod( | ||
"copyMemory", | ||
parameterTypes = Seq("java.lang.Object", "long", "java.lang.Object", "long", "long")), | ||
ReflectMethod("getAndAddLong", parameterTypes = Seq("java.lang.Object", "long", "long")), | ||
ReflectMethod("getAndSetObject", parameterTypes = Seq("java.lang.Object", "long", "java.lang.Object")), | ||
ReflectMethod("getBoolean", parameterTypes = Seq("java.lang.Object", "long")), | ||
ReflectMethod("getByte", parameterTypes = Seq("long")), | ||
ReflectMethod("getByte", parameterTypes = Seq("java.lang.Object", "long")), | ||
ReflectMethod("getDouble", parameterTypes = Seq("java.lang.Object", "long")), | ||
ReflectMethod("getFloat", parameterTypes = Seq("java.lang.Object", "long")), | ||
ReflectMethod("getInt", parameterTypes = Seq("long")), | ||
ReflectMethod("getInt", parameterTypes = Seq("java.lang.Object", "long")), | ||
ReflectMethod("getLong", parameterTypes = Seq("long")), | ||
ReflectMethod("getLong", parameterTypes = Seq("java.lang.Object", "long")), | ||
ReflectMethod("getObject", parameterTypes = Seq("java.lang.Object", "long")), | ||
ReflectMethod("invokeCleaner", parameterTypes = Seq("java.nio.ByteBuffer")), | ||
ReflectMethod("objectFieldOffset", parameterTypes = Seq("java.lang.reflect.Field")), | ||
ReflectMethod("putBoolean", parameterTypes = Seq("java.lang.Object", "long", "boolean")), | ||
ReflectMethod("putByte", parameterTypes = Seq("long", "byte")), | ||
ReflectMethod("putByte", parameterTypes = Seq("java.lang.Object", "long", "byte")), | ||
Check failure on line 55 in akka-actor-tests/src/test/scala/akka/NativeImageMetadataSpec.scala
|
||
ReflectMethod("putDouble", parameterTypes = Seq("java.lang.Object", "long", "double")), | ||
ReflectMethod("putFloat", parameterTypes = Seq("java.lang.Object", "long", "float")), | ||
ReflectMethod("putInt", parameterTypes = Seq("long", "int")), | ||
ReflectMethod("putInt", parameterTypes = Seq("java.lang.Object", "long", "int")), | ||
ReflectMethod("putLong", parameterTypes = Seq("long", "long")), | ||
ReflectMethod("putLong", parameterTypes = Seq("java.lang.Object", "long", "long")), | ||
ReflectMethod("putObject", parameterTypes = Seq("java.lang.Object", "long", "java.lang.Object")), | ||
ReflectMethod("storeFence", parameterTypes = Seq())), | ||
allDeclaredFields = true), | ||
// FIXME these are mostly "static" trixery, I wonder if graal can't figure them out itself without explicit listing | ||
ReflectConfigEntry( | ||
classOf[akka.actor.ActorCell].getName, | ||
fields = Seq( | ||
ReflectField("akka$actor$dungeon$Children$$_childrenRefsDoNotCallMeDirectly"), | ||
ReflectField("akka$actor$dungeon$Children$$_functionRefsDoNotCallMeDirectly"), | ||
ReflectField("akka$actor$dungeon$Children$$_nextNameDoNotCallMeDirectly"), | ||
ReflectField("akka$actor$dungeon$Dispatch$$_mailboxDoNotCallMeDirectly"))), | ||
ReflectConfigEntry( | ||
classOf[akka.actor.RepointableRef].getName, | ||
fields = Seq(ReflectField("_cellDoNotCallMeDirectly"), ReflectField("_lookupDoNotCallMeDirectly"))), | ||
ReflectConfigEntry( | ||
classOf[akka.pattern.CircuitBreaker].getName, | ||
fields = Seq( | ||
ReflectField("_currentResetTimeoutDoNotCallMeDirectly"), | ||
ReflectField("_currentStateDoNotCallMeDirectly"))), | ||
ReflectConfigEntry( | ||
classOf[akka.pattern.PromiseActorRef].getName, | ||
fields = Seq(ReflectField("_stateDoNotCallMeDirectly"), ReflectField("_watchedByDoNotCallMeDirectly"))), | ||
ReflectConfigEntry("akka.actor.LightArrayRevolverScheduler$TaskHolder", fields = Seq(ReflectField("task"))), | ||
// loaded via config | ||
ReflectConfigEntry( | ||
"akka.actor.LocalActorRefProvider$Guardian", | ||
queryAllDeclaredConstructors = true, | ||
methods = Seq(ReflectMethod(Constructor, Seq("akka.actor.SupervisorStrategy")))), | ||
ReflectConfigEntry( | ||
"akka.actor.LocalActorRefProvider$SystemGuardian", | ||
queryAllDeclaredConstructors = true, | ||
methods = Seq(ReflectMethod(Constructor, Seq("akka.actor.SupervisorStrategy", "akka.actor.ActorRef")))), | ||
ReflectConfigEntry( | ||
classOf[LocalActorRefProvider].getName, | ||
methods = Seq( | ||
ReflectMethod( | ||
Constructor, | ||
Seq( | ||
classOf[java.lang.String].getName, | ||
classOf[ActorSystem.Settings].getName, | ||
classOf[EventStream].getName, | ||
classOf[DynamicAccess].getName)))), | ||
// left as reflection based to not break akka-remote remote deploy test | ||
ReflectConfigEntry(classOf[Props.EmptyActor].getName, methods = Seq(ReflectMethod(Constructor))), | ||
// affinity pool pluggable things | ||
ReflectConfigEntry( | ||
"akka.dispatch.affinity.ThrowOnOverflowRejectionHandler", | ||
methods = Seq(ReflectMethod(Constructor, Seq.empty))), | ||
ReflectConfigEntry( | ||
"akka.dispatch.affinity.FairDistributionHashCache", | ||
methods = Seq(ReflectMethod(Constructor, Seq("com.typesafe.config.Config")))), | ||
// logging infra | ||
ReflectConfigEntry( | ||
classOf[akka.event.Logging.DefaultLogger].getName, | ||
methods = Seq(ReflectMethod(NativeImageUtils.Constructor))), | ||
ReflectConfigEntry( | ||
classOf[akka.event.DefaultLoggingFilter].getName, | ||
methods = Seq( | ||
ReflectMethod( | ||
NativeImageUtils.Constructor, | ||
parameterTypes = Seq("akka.actor.ActorSystem$Settings", "akka.event.EventStream")))), | ||
// akka io stuff | ||
ReflectConfigEntry( | ||
classOf[akka.io.InetAddressDnsProvider].getName, | ||
methods = Seq(ReflectMethod(NativeImageUtils.Constructor))), | ||
// FIXME remove these DNS related entries when removing the deprecated pluggable DNS setup and switch to non-reflective construction | ||
// pluggable through deprecated InetAddressDnsProvider | ||
ReflectConfigEntry( | ||
classOf[akka.io.SimpleDnsManager].getName, | ||
methods = Seq(ReflectMethod(NativeImageUtils.Constructor, parameterTypes = Seq("akka.io.DnsExt"))), | ||
queryAllDeclaredConstructors = true), | ||
ReflectConfigEntry( | ||
classOf[akka.io.dns.internal.AsyncDnsProvider].getName, | ||
methods = Seq(ReflectMethod(NativeImageUtils.Constructor, parameterTypes = Seq("akka.io.DnsExt"))), | ||
queryAllDeclaredConstructors = true), | ||
// pluggable through deprecated InetAddressDnsProvider.actorClass | ||
ReflectConfigEntry( | ||
classOf[akka.io.InetAddressDnsResolver].getName, | ||
methods = Seq( | ||
ReflectMethod( | ||
NativeImageUtils.Constructor, | ||
parameterTypes = Seq("akka.io.SimpleDnsCache", "com.typesafe.config.Config"))), | ||
queryAllDeclaredConstructors = true), | ||
ReflectConfigEntry( | ||
"akka.io.dns.internal.AsyncDnsResolver", | ||
methods = Seq( | ||
ReflectMethod( | ||
NativeImageUtils.Constructor, | ||
parameterTypes = Seq( | ||
"akka.io.SimpleDnsCache", | ||
"com.typesafe.config.Config", | ||
"scala.Function2<akka.actor.ActorRefFactory,scala.collection.immutable.List<java.net.InetSocketAddress>,scala.collection.immutable.List<akka.actor.ActorRef>>"))), | ||
queryAllDeclaredConstructors = true), | ||
// pluggable through deprecated InetAddressDnsProvider.managerClass | ||
ReflectConfigEntry( | ||
classOf[akka.io.SimpleDnsManager].getName, | ||
methods = Seq(ReflectMethod(NativeImageUtils.Constructor, parameterTypes = Seq("akka.io.DnsExt"))), | ||
queryAllDeclaredConstructors = true), | ||
ReflectConfigEntry( | ||
"akka.io.dns.internal.AsyncDnsManager", | ||
methods = Seq(ReflectMethod(NativeImageUtils.Constructor, parameterTypes = Seq("akka.io.DnsExt"))), | ||
queryAllDeclaredConstructors = true), | ||
// Internal Routing infra | ||
ReflectConfigEntry( | ||
classOf[akka.routing.RoutedActorCell.RouterActorCreator].getName, | ||
methods = Seq(ReflectMethod(NativeImageUtils.Constructor, parameterTypes = Seq(classOf[RouterConfig].getName))))) ++ serializationBindingTypeEntries | ||
|
||
def serializationBindingTypeEntries = { | ||
// we bind a bunch of stdlib and primitive types in akka-core (not what you'd normally do) | ||
val config = ConfigFactory.load() | ||
config.getConfig("akka.actor.serialization-bindings").root().unwrapped().keySet().asScala.map { typeName => | ||
ReflectConfigEntry(typeName) | ||
} | ||
} | ||
|
||
val modulePackages = Seq( | ||
"akka.actor", | ||
"akka.dispatch", | ||
"akka.event", | ||
"akka.io", | ||
"akka.japi", | ||
"akka.pattern", | ||
"akka.routing", | ||
"akka.serialization", | ||
"akka.util") | ||
|
||
// run this to regenerate metadata 'akka-actor-tests/Test/runMain akka.NativeImageMetadataSpec' | ||
def main(args: Array[String]): Unit = { | ||
NativeImageUtils.writeMetadata(metadataDir, additionalEntries, modulePackages) | ||
} | ||
} | ||
|
||
class NativeImageMetadataSpec extends AnyWordSpec with Matchers { | ||
import NativeImageMetadataSpec._ | ||
|
||
"Native-image metadata for akka-actor" should { | ||
|
||
"be up to date" in { | ||
val (existing, current) = NativeImageUtils.verifyMetadata(metadataDir, additionalEntries, modulePackages) | ||
existing should ===(current) | ||
} | ||
} | ||
|
||
} |
56 changes: 56 additions & 0 deletions
56
akka-actor-typed-tests/src/test/scala/akka/actor/typed/NativeImageMetadataSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* Copyright (C) 2009-2023 Lightbend Inc. <https://www.lightbend.com> | ||
*/ | ||
|
||
package akka.actor.typed | ||
|
||
import akka.testkit.NativeImageUtils | ||
import akka.testkit.NativeImageUtils.ModuleField | ||
import akka.testkit.NativeImageUtils.ReflectConfigEntry | ||
import akka.testkit.NativeImageUtils.ReflectField | ||
import org.scalatest.matchers.should.Matchers | ||
import org.scalatest.wordspec.AnyWordSpec | ||
|
||
object NativeImageMetadataSpec { | ||
|
||
val metadataDir = NativeImageUtils.metadataDirFor("akka-actor-typed") | ||
|
||
val additionalEntries = Seq( | ||
ReflectConfigEntry( | ||
"akka.actor.typed.internal.adapter.ActorSystemAdapter$LoadTypedExtensions$", | ||
fields = Seq(ReflectField("MODULE$"))), | ||
// trixery around auto-selecting local or cluster receptionist impl | ||
ReflectConfigEntry( | ||
classOf[akka.actor.typed.internal.receptionist.LocalReceptionist.type].getName, | ||
fields = Seq(ModuleField))) ++ serializationBindingTypeEntries | ||
|
||
val modulePackages = Seq("akka.actor.typed") | ||
|
||
def serializationBindingTypeEntries = { | ||
// Note: can't load from config because we'd get entries from all the other modules on classpath | ||
Set( | ||
"akka.actor.typed.ActorRef", | ||
"akka.actor.typed.internal.adapter.ActorRefAdapter", | ||
"akka.actor.typed.internal.receptionist.DefaultServiceKey").map { typeName => | ||
ReflectConfigEntry(typeName) | ||
} | ||
} | ||
|
||
// run this to regenerate metadata 'akka-actor-typed-tests/Test/runMain akka.actor.typed.NativeImageMetadataSpec' | ||
def main(args: Array[String]): Unit = { | ||
NativeImageUtils.writeMetadata(metadataDir, additionalEntries, modulePackages) | ||
} | ||
} | ||
|
||
class NativeImageMetadataSpec extends AnyWordSpec with Matchers { | ||
import NativeImageMetadataSpec._ | ||
|
||
"Native-image metadata for akka-actor-typed" should { | ||
|
||
"be up to date" in { | ||
val (existing, current) = NativeImageUtils.verifyMetadata(metadataDir, additionalEntries, modulePackages) | ||
existing should ===(current) | ||
} | ||
} | ||
|
||
} |
69 changes: 69 additions & 0 deletions
69
...in/resources/META-INF/native-image/com/typesafe/akka/akka-actor-typed/reflect-config.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
[ { | ||
"name" : "akka.actor.typed.internal.adapter.ActorSystemAdapter$LoadTypedExtensions$", | ||
"fields" : [ { | ||
"name" : "MODULE$" | ||
} ] | ||
}, { | ||
"name" : "akka.actor.typed.internal.receptionist.LocalReceptionist$", | ||
"fields" : [ { | ||
"name" : "MODULE$" | ||
} ] | ||
}, { | ||
"name" : "akka.actor.typed.ActorRef" | ||
}, { | ||
"name" : "akka.actor.typed.internal.adapter.ActorRefAdapter" | ||
}, { | ||
"name" : "akka.actor.typed.internal.receptionist.DefaultServiceKey" | ||
}, { | ||
"name" : "akka.actor.typed.internal.adapter.ActorSystemAdapter$AdapterExtension$", | ||
"fields" : [ { | ||
"name" : "MODULE$" | ||
} ] | ||
}, { | ||
"name" : "akka.actor.typed.internal.adapter.ActorSystemAdapter$LoadTypedExtensions$", | ||
"fields" : [ { | ||
"name" : "MODULE$" | ||
} ] | ||
}, { | ||
"name" : "akka.actor.typed.internal.adapter.AdapterExtension$", | ||
"fields" : [ { | ||
"name" : "MODULE$" | ||
} ] | ||
}, { | ||
"name" : "akka.actor.typed.internal.MiscMessageSerializer", | ||
"methods" : [ { | ||
"name" : "<init>", | ||
"parameterTypes" : [ "akka.actor.ExtendedActorSystem" ] | ||
} ] | ||
}, { | ||
"name" : "akka.actor.typed.internal.receptionist.ServiceKeySerializer", | ||
"methods" : [ { | ||
"name" : "<init>", | ||
"parameterTypes" : [ "akka.actor.ExtendedActorSystem" ] | ||
} ] | ||
}, { | ||
"name" : "akka.actor.typed.ActorRefResolver$", | ||
"fields" : [ { | ||
"name" : "MODULE$" | ||
} ] | ||
}, { | ||
"name" : "akka.actor.typed.internal.ActorFlightRecorder$", | ||
"fields" : [ { | ||
"name" : "MODULE$" | ||
} ] | ||
}, { | ||
"name" : "akka.actor.typed.internal.EventStreamExtension$", | ||
"fields" : [ { | ||
"name" : "MODULE$" | ||
} ] | ||
}, { | ||
"name" : "akka.actor.typed.pubsub.PubSub$", | ||
"fields" : [ { | ||
"name" : "MODULE$" | ||
} ] | ||
}, { | ||
"name" : "akka.actor.typed.receptionist.Receptionist$", | ||
"fields" : [ { | ||
"name" : "MODULE$" | ||
} ] | ||
} ] |
1 change: 1 addition & 0 deletions
1
...main/resources/META-INF/native-image/com/typesafe/akka/akka-actor/native-image.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Args = --enable-monitoring=jfr |
Oops, something went wrong.