Skip to content

Commit 67387c0

Browse files
committed
Merge branch 'dev'
2 parents e2efe61 + 186eae3 commit 67387c0

File tree

94 files changed

+1508
-1355
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+1508
-1355
lines changed

.github/workflows/ci.yaml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: LambdaRPC CI
2+
3+
on:
4+
pull_request:
5+
6+
jobs:
7+
test:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v2
11+
- name: Set up JDK 11
12+
uses: actions/setup-java@v2
13+
with:
14+
java-version: '11'
15+
distribution: 'temurin'
16+
- name: Build lambdarpc-core
17+
uses: gradle/gradle-build-action@937999e9cc2425eddc7fd62d1053baf041147db7
18+
with:
19+
arguments: |
20+
lambdarpc-core:build
21+
- name: Build examples
22+
uses: gradle/gradle-build-action@937999e9cc2425eddc7fd62d1053baf041147db7
23+
with:
24+
arguments: |
25+
examples:build

README.adoc

+22-25
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,10 @@ Simple native RPC with high order functions support.
77
== Service as a library
88

99
.λRPC makes multi-process communication smooth enough to recognize remote service like a common library. It is powered by two main ideas:
10-
* λRPC does not use standalone declarations (https://en.wikipedia.org/wiki/Interface_description_language[IDL])
11-
and code generation (so λRPC declarations are native for the user's code).
12-
Instead it allows working with library-specific data structures and default or custom serializers.
10+
* λRPC does not use standalone declarations (https://en.wikipedia.org/wiki/Interface_description_language[IDL]) and code generation (so λRPC declarations are native for the user's code).
11+
Instead, it allows working with library-specific data structures and default or custom serializers.
1312
* λRPC functions can receive and return other functions as first-class objects.
14-
Passed lambda will be executed on the client-side, so it can easily capture the state (even mutable)
15-
and be "sent" to the other language's process.
13+
Passed lambda will be executed on the client-side, so it can easily capture the state (even mutable) and be "sent" to the other language's process.
1614

1715
== Example
1816

@@ -74,7 +72,7 @@ fun main() = runBlocking(serviceDispatcher) {
7472
val rawModel = Model(...)
7573
// Bind dataloader with dataEndpoint, so mlservice will communicate directly
7674
// with the dataservice on the dataEndpoint without client in the middle
77-
val boundLoader = bf(dataloader)
75+
val boundLoader = dataloader.toBound()
7876
val model = fit(rawModel, boundLoader) { epoch, metric ->
7977
// Lambda will be executed on the client site -- the λRPC magic
8078
println("Epoch = $epoch, metric = $metric")
@@ -124,10 +122,8 @@ image::https://user-images.githubusercontent.com/25281147/153264790-74784fb7-3be
124122
* Interactive computations: computing function receives closure as a parameter and calls it periodically, providing computation status information and receiving further directives.
125123
* Security:
126124
** Send closures operating on the sensitive data instead of the data itself.
127-
** Provide computational resources as a library of functions that are parametrized by client lambdas instead of
128-
receiving the client's code and executing it.
129-
* Choose dynamically computation location: compute something that uses a large amount of data on a client or send data to the server and
130-
compute there.
125+
** Provide computational resources as a library of functions that are parametrized by client lambdas instead of receiving the client's code and executing it.
126+
* Choose dynamically computation location: compute something that uses a large amount of data on a client or send data to the server and compute there.
131127
* Load balancing: once a task is finished, request new via client's lambda.
132128
* Stateful streaming computations: nodes provide their lambdas for a mapper.
133129

@@ -143,53 +139,54 @@ Then λRPC provides efficient communication with the corresponding backend part.
143139

144140
== Getting started
145141

146-
.Run both `:detekt` and `:test` tasks
142+
.Build and run fast tests and checks
147143
[source,bash]
148144
----
149-
$ ./gradlew :lambdarpc:check
145+
$ ./gradlew build
150146
----
151147

152148
.Run stress tests
153149
[source,bash]
154150
----
155-
$ ./gradlew :lambdarpc:slow
151+
$ ./gradlew :lambdarpc-core:slow
156152
----
157153

158154
.Generate documentation
159155
[source,bash]
160156
----
161-
$ ./gradlew :lambdarpc:dokkaHtml
162-
$ cd ./lambdarpc/build/dokka/html
157+
$ ./gradlew :lambdarpc-core:dokkaHtml
158+
$ cd ./lambdarpc-core/build/dokka/html
163159
----
164160

165161
=== Repository organization
166162

167163
.examples
168-
* `ml` -- readme example.
164+
* `interactive_ml` -- readme example.
165+
169166
[sources,bash]
170167
----
171168
$ cd LambdaRPC.kt
172169
$ ./gradlew :examples:ml.dataservice
173170
$ ./gradlew :examples:ml.mlservice
174171
$ ./gradlew :examples:ml.client
175172
----
176-
* `lazy` -- an interesting example that shows the possibility to build lazy
177-
data processing pipelines using common λRPC functionality.
173+
174+
* `promise_pipeline` -- an interesting example that shows the possibility to build lazy data processing pipelines using common λRPC functionality.
175+
178176
[sources,bash]
179177
----
180178
$ cd LambdaRPC.kt
181-
$ ./gradlew :examples:lazy.service --args=8090
182-
$ ./gradlew :examples:lazy.service --args=8091
179+
$ ./gradlew :examples:promise.service --args=8090
180+
$ ./gradlew :examples:promise.service --args=8091
183181
# Any number of services on different ports
184-
$ ./gradlew :examples:lazy.client --args='8090 8091' # Ports of all services
182+
$ ./gradlew :examples:promise.client --args='8090 8091' # Ports of all services
185183
----
186184

187-
.lambdarpc
185+
.lambdarpc-core
188186
* `dsl` -- domain-specific language for λRPC library users.
189-
* `exceptions` -- base λRPC exception classes.
190187
* `functions` -- λRPC functions: backend and frontend parts.
191-
* `coders` -- data coder (serializer) and function coder.
192-
** λRPC provides some default data coders based on `kotlinx.serialization`, but users can also implement thier own.
188+
* `coding` -- contains `Coder` definition, it is a thing that can serialize data and work with functions.
189+
** λRPC provides some default data coders based on `kotlinx.serialization`, but users can also implement their own.
193190
** Function encoding saves language closure as backend function to the registry with some `access name`.
194191
Function decoding creates a frontend function that communicates with the corresponding backend function.
195192
* `service` -- libservice implementation.

examples/build.gradle.kts

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,28 @@ tasks.withType<KotlinCompile>().all {
1515

1616
dependencies {
1717
implementation(kotlin("stdlib"))
18-
implementation(project(":lambdarpc"))
18+
implementation(project(":lambdarpc-core"))
1919
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
2020
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
2121
implementation("org.slf4j:slf4j-simple:1.7.36")
2222
implementation("io.github.microutils:kotlin-logging-jvm:2.1.21")
2323
}
2424

25-
val lazy = "io.lambdarpc.examples.lazy"
25+
val lazy = "io.lambdarpc.examples.promise_pipeline"
2626

27-
tasks.register<JavaExec>("lazy.service") {
27+
tasks.register<JavaExec>("promise.service") {
2828
dependsOn("classes")
2929
classpath = sourceSets["main"].runtimeClasspath
3030
mainClass.set("$lazy.service.ServiceKt")
3131
}
3232

33-
tasks.register<JavaExec>("lazy.client") {
33+
tasks.register<JavaExec>("promise.client") {
3434
dependsOn("classes")
3535
classpath = sourceSets["main"].runtimeClasspath
3636
mainClass.set("$lazy.client.ClientKt")
3737
}
3838

39-
val ml = "io.lambdarpc.examples.ml"
39+
val ml = "io.lambdarpc.examples.interactive_ml"
4040

4141
tasks.register<JavaExec>("ml.dataservice") {
4242
dependsOn("classes")

examples/src/main/kotlin/io/lambdarpc/examples/ml/Defs.kt examples/src/main/kotlin/io/lambdarpc/examples/interactive_ml/Defs.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.lambdarpc.examples.ml
1+
package io.lambdarpc.examples.interactive_ml
22

33
import io.lambdarpc.utils.Endpoint
44

examples/src/main/kotlin/io/lambdarpc/examples/ml/client/Client.kt examples/src/main/kotlin/io/lambdarpc/examples/interactive_ml/client/Client.kt

+13-13
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
package io.lambdarpc.examples.ml.client
1+
package io.lambdarpc.examples.interactive_ml.client
22

33
import io.lambdarpc.dsl.ServiceDispatcher
4-
import io.lambdarpc.dsl.bf
5-
import io.lambdarpc.examples.ml.dataEndpoint
6-
import io.lambdarpc.examples.ml.dataservice.facade.dataLoader
7-
import io.lambdarpc.examples.ml.dataservice.facade.dataServiceId
8-
import io.lambdarpc.examples.ml.mlEndpoint
9-
import io.lambdarpc.examples.ml.mlservice.Metric
10-
import io.lambdarpc.examples.ml.mlservice.Model
11-
import io.lambdarpc.examples.ml.mlservice.facade.fit
12-
import io.lambdarpc.examples.ml.mlservice.facade.mlServiceId
4+
import io.lambdarpc.dsl.blockingConnectionPool
5+
import io.lambdarpc.dsl.toBound
6+
import io.lambdarpc.examples.interactive_ml.dataEndpoint
7+
import io.lambdarpc.examples.interactive_ml.dataservice.facade.dataLoader
8+
import io.lambdarpc.examples.interactive_ml.dataservice.facade.dataServiceId
9+
import io.lambdarpc.examples.interactive_ml.mlEndpoint
10+
import io.lambdarpc.examples.interactive_ml.mlservice.Metric
11+
import io.lambdarpc.examples.interactive_ml.mlservice.Model
12+
import io.lambdarpc.examples.interactive_ml.mlservice.facade.fit
13+
import io.lambdarpc.examples.interactive_ml.mlservice.facade.mlServiceId
1314
import io.lambdarpc.transport.MapServiceRegistry
14-
import kotlinx.coroutines.runBlocking
1515

1616
/**
1717
* Can be a simple map that service or client receives with command line options,
@@ -24,14 +24,14 @@ val serviceRegistry = MapServiceRegistry(
2424

2525
val serviceDispatcher = ServiceDispatcher(serviceRegistry)
2626

27-
fun main() = runBlocking(serviceDispatcher) {
27+
fun main() = blockingConnectionPool(serviceDispatcher) {
2828
// Keep track of the loss function values
2929
val history = mutableListOf<Metric>()
3030
var lastEpoch = 0
3131
val rawModel = Model()
3232
// Bind dataloader with dataEndpoint, so mlservice will communicate directly
3333
// with the dataservice on the dataEndpoint without client in the middle
34-
val boundLoader = bf(dataLoader)
34+
val boundLoader = dataLoader.toBound()
3535
val model = fit(rawModel, boundLoader) { epoch, metric ->
3636
// Lambda will be executed on the client site -- the λRPC magic
3737
println("Epoch = $epoch, metric = $metric")

examples/src/main/kotlin/io/lambdarpc/examples/ml/dataservice/Facade.kt examples/src/main/kotlin/io/lambdarpc/examples/interactive_ml/dataservice/Facade.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
@file:Suppress("PackageDirectoryMismatch")
22

3-
package io.lambdarpc.examples.ml.dataservice.facade
3+
package io.lambdarpc.examples.interactive_ml.dataservice.facade
44

55
import io.lambdarpc.dsl.def
66
import io.lambdarpc.dsl.j
7-
import io.lambdarpc.examples.ml.mlservice.Data
7+
import io.lambdarpc.examples.interactive_ml.mlservice.Data
88
import io.lambdarpc.utils.toSid
99

1010
val dataServiceId = "45b33c9c-ae42-4835-b27c-ad6f57b6a82d".toSid()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package io.lambdarpc.examples.interactive_ml.dataservice
2+
3+
import io.lambdarpc.examples.interactive_ml.mlservice.Data
4+
5+
fun dataloader(): Data = Data(1)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.lambdarpc.examples.interactive_ml.dataservice
2+
3+
import io.lambdarpc.dsl.LibService
4+
import io.lambdarpc.examples.interactive_ml.dataEndpoint
5+
import io.lambdarpc.examples.interactive_ml.dataservice.facade.dataLoader
6+
import io.lambdarpc.examples.interactive_ml.dataservice.facade.dataServiceId
7+
8+
fun main() {
9+
val service = LibService(dataServiceId, dataEndpoint) {
10+
dataLoader of ::dataloader
11+
}
12+
service.start()
13+
service.awaitTermination()
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
@file:Suppress("PackageDirectoryMismatch")
2+
3+
package io.lambdarpc.examples.interactive_ml.mlservice.facade
4+
5+
import io.lambdarpc.coding.Coder
6+
import io.lambdarpc.coding.CodingContext
7+
import io.lambdarpc.dsl.def
8+
import io.lambdarpc.dsl.f
9+
import io.lambdarpc.dsl.j
10+
import io.lambdarpc.examples.interactive_ml.mlservice.Data
11+
import io.lambdarpc.examples.interactive_ml.mlservice.Epoch
12+
import io.lambdarpc.examples.interactive_ml.mlservice.Metric
13+
import io.lambdarpc.examples.interactive_ml.mlservice.Model
14+
import io.lambdarpc.transport.grpc.Entity
15+
import io.lambdarpc.transport.serialization.Entity
16+
import io.lambdarpc.transport.serialization.RawData
17+
import io.lambdarpc.utils.toSid
18+
19+
val mlServiceId = "1a897419-0fd2-4e84-976f-0a2211a48898".toSid()
20+
21+
private val loader = f( // Coder for the function: suspend () -> Data
22+
j<Data>() // kotlinx.serialization JSON coder for the @Serializable Data
23+
)
24+
25+
val fit by mlServiceId.def( // Define declaration for suspend (Epoch, Metric) -> Boolean
26+
ModelCoder, // Model may not be @Serializable, so λRPC allows writing custom data coders
27+
loader, f(j<Epoch>(), j<Metric>(), j<Boolean>()),
28+
ModelCoder
29+
)
30+
31+
private object ModelCoder : Coder<Model> {
32+
override fun encode(value: Model, context: CodingContext): Entity =
33+
Entity(RawData.copyFrom(byteArrayOf(value.weight.toByte())))
34+
35+
override fun decode(entity: Entity, context: CodingContext): Model =
36+
Model(entity.data.toByteArray().first().toInt())
37+
}

examples/src/main/kotlin/io/lambdarpc/examples/ml/mlservice/Lib.kt examples/src/main/kotlin/io/lambdarpc/examples/interactive_ml/mlservice/Lib.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.lambdarpc.examples.ml.mlservice
1+
package io.lambdarpc.examples.interactive_ml.mlservice
22

33
import kotlinx.coroutines.delay
44
import kotlinx.coroutines.runBlocking

examples/src/main/kotlin/io/lambdarpc/examples/ml/mlservice/Service.kt examples/src/main/kotlin/io/lambdarpc/examples/interactive_ml/mlservice/Service.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
package io.lambdarpc.examples.ml.mlservice
1+
package io.lambdarpc.examples.interactive_ml.mlservice
22

33
import io.lambdarpc.dsl.LibService
4-
import io.lambdarpc.examples.ml.mlEndpoint
5-
import io.lambdarpc.examples.ml.mlservice.facade.fit
6-
import io.lambdarpc.examples.ml.mlservice.facade.mlServiceId
4+
import io.lambdarpc.examples.interactive_ml.mlEndpoint
5+
import io.lambdarpc.examples.interactive_ml.mlservice.facade.fit
6+
import io.lambdarpc.examples.interactive_ml.mlservice.facade.mlServiceId
77

88
fun main() {
99
// Such service that looks like a library is called libservice

examples/src/main/kotlin/io/lambdarpc/examples/ml/dataservice/Lib.kt

-5
This file was deleted.

examples/src/main/kotlin/io/lambdarpc/examples/ml/dataservice/Service.kt

-14
This file was deleted.

examples/src/main/kotlin/io/lambdarpc/examples/ml/mlservice/Facade.kt

-35
This file was deleted.

examples/src/main/kotlin/io/lambdarpc/examples/lazy/Defs.kt examples/src/main/kotlin/io/lambdarpc/examples/promise_pipeline/Defs.kt

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
@file:Suppress("SpellCheckingInspection")
22

3-
package io.lambdarpc.examples.lazy
3+
package io.lambdarpc.examples.promise_pipeline
44

5-
import io.lambdarpc.coders.Coder
5+
import io.lambdarpc.coding.Coder
66
import io.lambdarpc.dsl.f
77
import io.lambdarpc.dsl.j
8-
import io.lambdarpc.functions.frontend.ConnectedFunction
98
import kotlinx.coroutines.async
109
import kotlinx.coroutines.coroutineScope
1110

@@ -20,15 +19,12 @@ fun <R> lazify(f: suspend () -> R): suspend () -> Promise<R> = {
2019
fun <A, R> lazify(
2120
f: suspend (A) -> R
2221
): suspend (Promise<A>) -> Promise<R> = { a ->
23-
require(a is ConnectedFunction);
2422
{ f(a()) }
2523
}
2624

2725
fun <A, B, R> lazify(
2826
f: suspend (A, B) -> R
2927
): suspend (Promise<A>, Promise<B>) -> Promise<R> = { a, b ->
30-
require(a is ConnectedFunction)
31-
require(b is ConnectedFunction);
3228
{
3329
coroutineScope {
3430
val aa = async { a() }

0 commit comments

Comments
 (0)