Skip to content

Commit 69abe1c

Browse files
authored
Support scala version cross builds (#379)
## Summary Changes to support builds/tests with both scala 2.12 and 2.13 versions. By default we build against 2.12 version, pass "--config scala_2.13" option to "bazel build/test" to override it. ScalaFmt seems to be breaking for 2.13 using bazel rules_scala package, [fix](bazel-contrib/rules_scala#1631) is already deployed but a release with that change is not available yet, so temporarily disabled ScalaFmt checks for 2.13 will enable later once the fix is released. ## Checklist - [ ] Added Unit Tests - [x] Covered by existing CI - [ ] Integration tested - [ ] Documentation update <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Enabled flexible Scala version selection (2.12 and 2.13) for smoother builds and enhanced compatibility. - Introduced a default Scala version constant and a repository rule for improved version management. - Added support for additional Scala 2.13 dependencies in the build configuration. - **Refactor and Improvements** - Streamlined build and dependency management for increased stability and performance. - Consolidated collection conversion utilities to boost reliability in tests and runtime processing. - Enhanced type safety and clarity in collection handling across various modules. - Improved handling of Scala collections and maps throughout the codebase for better type consistency and safety. - Updated method implementations to ensure explicit type conversions, enhancing clarity and preventing runtime errors. - Modified method signatures and internal logic to utilize `Seq` for improved type clarity and consistency. - Enhanced the `maven_artifact` function to accept an optional version parameter for better dependency management. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent c7635b8 commit 69abe1c

File tree

61 files changed

+334
-199
lines changed

Some content is hidden

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

61 files changed

+334
-199
lines changed

.bazelrc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
try-import %workspace%/.bazelrc.local
22

3+
# To build with Scala 2.12, pass "--config scala_2.12" to "bazel build"
4+
common:scala_2.12 --repo_env=SCALA_VERSION=2.12.18
5+
common:scala_2.12 --define=SCALA_VERSION=2.12.18
6+
common:scala_2.13 --repo_env=SCALA_VERSION=2.13.12
7+
common:scala_2.13 --define=SCALA_VERSION=2.13.12
8+
9+
# Default scala version to 2.12
10+
# To set a different default Scala version, add the following to .bazelrc.local:
11+
# common --config scala_2.12
12+
common --repo_env=SCALA_VERSION=2.12.18
13+
common --define=SCALA_VERSION=2.12.18
14+
315
build --java_language_version=11
416
build --java_runtime_version=11
517
build --remote_cache=https://storage.googleapis.com/zipline-bazel-cache

.scalafmt.conf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
version = 2.5.0
1+
version = 3.8.3
2+
runner.dialect = scala212
23
align.openParenCallSite = true
34
align.openParenDefnSite = true
45
danglingParentheses.defnSite = false

WORKSPACE

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
workspace(name = "chronon")
22

3-
# Scala version used across the project
4-
SCALA_VERSION = "2.12.18"
5-
63
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
74

5+
# Load scala version from the config
6+
load("//:scala_config.bzl", "scala_version")
7+
scala_version(name = "scala_config")
8+
load("@scala_config//:version.bzl", "SCALA_VERSION")
9+
810
# Contains useful bazel utility functions and rules
911
http_archive(
1012
name = "bazel_skylib",
@@ -74,22 +76,8 @@ http_archive(
7476

7577
# Initialize Scala with specific version support
7678
load("@io_bazel_rules_scala//:scala_config.bzl", "scala_config")
77-
7879
scala_config(scala_version = SCALA_VERSION)
7980

80-
load("@io_bazel_rules_scala//scala:scala_maven_import_external.bzl", "scala_maven_import_external")
81-
82-
scala_maven_import_external(
83-
name = "scala_compiler_source_2_12_18",
84-
artifact = "org.scala-lang:scala-compiler:%s:sources" % SCALA_VERSION,
85-
artifact_sha256 = "f79ee80f140218253f2a38c9d73f8a9b552d06afce7a5f61cf08079a388e21df",
86-
licenses = ["notice"],
87-
server_urls = [
88-
"https://repo1.maven.org/maven2",
89-
"https://mirror.bazel.build/repo1.maven.org/maven2",
90-
],
91-
)
92-
9381
load("@io_bazel_rules_scala//scala:scala.bzl", "scala_repositories")
9482
scala_repositories()
9583

aggregator/BUILD.bazel

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
scala_library(
22
name = "lib",
33
srcs = glob(["src/main/**/*.scala"]),
4-
format = True,
4+
format = select({
5+
"//tools/config:scala_2_13": False, # Disable for 2.13
6+
"//conditions:default": True, # Enable for other versions
7+
}),
58
visibility = ["//visibility:public"],
69
deps = [
710
"//api:lib",
@@ -50,7 +53,10 @@ test_deps = [
5053
scala_library(
5154
name = "test_lib",
5255
srcs = glob(["src/test/**/*.scala"]),
53-
format = True,
56+
format = select({
57+
"//tools/config:scala_2_13": False, # Disable for 2.13
58+
"//conditions:default": True, # Enable for other versions
59+
}),
5460
visibility = ["//visibility:public"],
5561
deps = test_deps,
5662
)

aggregator/src/main/scala/ai/chronon/aggregator/row/StatsGenerator.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,12 @@ object StatsGenerator {
157157
val comparisonSketch = KllFloatsSketch.heapify(Memory.wrap(comparison.asInstanceOf[Array[Byte]]))
158158
val binsToDoubles = (0 to bins).map(_.toDouble / bins).toArray
159159
val keySet =
160-
referenceSketch.getQuantiles(binsToDoubles).union(comparisonSketch.getQuantiles(binsToDoubles)).distinct.sorted
160+
referenceSketch
161+
.getQuantiles(binsToDoubles)
162+
.union(comparisonSketch.getQuantiles(binsToDoubles))
163+
.distinct
164+
.sorted
165+
.toArray
161166
val referencePMF = regularize(referenceSketch.getPMF(keySet), eps)
162167
val comparisonPMF = regularize(comparisonSketch.getPMF(keySet), eps)
163168
var psi = 0.0

aggregator/src/test/scala/ai/chronon/aggregator/test/ApproxHistogramTest.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import org.junit.Assert._
66
import org.scalatest.flatspec.AnyFlatSpec
77

88
import java.util
9-
import scala.jdk.CollectionConverters._
9+
import ai.chronon.api.ScalaJavaConversions._
1010

1111
class ApproxHistogramTest extends AnyFlatSpec {
1212
it should "histogram" in {
@@ -144,10 +144,10 @@ class ApproxHistogramTest extends AnyFlatSpec {
144144
assertTrue(ir.sketch.isDefined)
145145

146146
val normalized = approxHistogram.denormalize(approxHistogram.normalize(ir))
147-
assertEquals(expected, approxHistogram.finalize(normalized).asScala)
147+
assertEquals(expected, approxHistogram.finalize(normalized).toScala)
148148
}
149149

150-
def toHashMap[T](map: Map[T, Long]): util.HashMap[T, Long] = new util.HashMap[T, Long](map.asJava)
150+
def toHashMap[T](map: Map[T, Long]): util.HashMap[T, Long] = new util.HashMap[T, Long](map.toJava)
151151

152152
def makeIr[T](agg: ApproxHistogram[T], counts: Map[T, Long]): ApproxHistogramIr[T] = {
153153
val values = counts.toSeq.sortBy(_._2)

aggregator/src/test/scala/ai/chronon/aggregator/test/FrequentItemsTest.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import org.junit.Assert._
88
import org.scalatest.flatspec.AnyFlatSpec
99

1010
import java.util
11-
import scala.jdk.CollectionConverters._
11+
import ai.chronon.api.ScalaJavaConversions._
1212

1313
class FrequentItemsTest extends AnyFlatSpec {
1414
it should "non power of two and truncate" in {
@@ -157,5 +157,5 @@ class FrequentItemsTest extends AnyFlatSpec {
157157
(sketch, ir)
158158
}
159159

160-
def toHashMap[T](map: Map[T, Long]): java.util.HashMap[T, Long] = new java.util.HashMap[T, Long](map.asJava)
160+
def toHashMap[T](map: Map[T, Long]): java.util.HashMap[T, Long] = new java.util.HashMap[T, Long](map.toJava)
161161
}

api/BUILD.bazel

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ scala_library(
2424
"src/main/**/*.scala",
2525
"src/main/**/*.java",
2626
]),
27-
format = True,
27+
format = select({
28+
"//tools/config:scala_2_13": False, # Disable for 2.13
29+
"//conditions:default": True, # Enable for other versions
30+
}),
2831
visibility = ["//visibility:public"],
2932
deps = [
3033
":thrift_java",
@@ -70,7 +73,10 @@ test_deps = [
7073
scala_library(
7174
name = "test-lib",
7275
srcs = glob(["src/test/**/*.scala"]),
73-
format = True,
76+
format = select({
77+
"//tools/config:scala_2_13": False, # Disable for 2.13
78+
"//conditions:default": True, # Enable for other versions
79+
}),
7480
visibility = ["//visibility:public"],
7581
deps = test_deps,
7682
)

api/src/main/scala/ai/chronon/api/Extensions.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -917,7 +917,7 @@ object Extensions {
917917

918918
def outputColumnsByGroup: Map[String, Array[String]] = {
919919
val preDeriveCols = (joinPartColumns ++ externalPartColumns)
920-
val preDerivedWithoutRenamed = preDeriveCols.mapValues(_.filterNot(renamedColumns.contains))
920+
val preDerivedWithoutRenamed = preDeriveCols.mapValues(_.filterNot(renamedColumns.contains)).toMap
921921
val derivedColumns: Array[String] = Option(join.derivations) match {
922922
case Some(derivations) => derivations.toScala.map { _.getName }.filter(_ == "*").toArray
923923
case None => Array.empty

api/src/main/scala/ai/chronon/api/ScalaJavaConversions.scala

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package ai.chronon.api
22

3-
import scala.collection.parallel.ParSeq
43
import scala.jdk.CollectionConverters._
4+
import scala.collection.Seq
55

66
object ScalaJavaConversions {
77

@@ -17,7 +17,7 @@ object ScalaJavaConversions {
1717
if (list == null) {
1818
null
1919
} else {
20-
list.asScala
20+
list.asScala.toSeq
2121
}
2222
}
2323

@@ -65,15 +65,6 @@ object ScalaJavaConversions {
6565
}
6666
}
6767
}
68-
implicit class IterableOps[T](it: Iterable[T]) {
69-
def parallel: ParSeq[T] = {
70-
if (it == null) {
71-
null
72-
} else {
73-
it.toSeq.par
74-
}
75-
}
76-
}
7768
implicit class MapOps[K, V](map: java.util.Map[K, V]) {
7869
def toScala: Map[K, V] = {
7970
if (map == null) {

api/src/test/scala/ai/chronon/api/test/TileSeriesSerializationTest.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package ai.chronon.api.test
22

33
import ai.chronon.api.Constants
4-
import ai.chronon.api.ScalaJavaConversions.JListOps
4+
import ai.chronon.api.ScalaJavaConversions._
55
import ai.chronon.api.ThriftJsonCodec
66
import ai.chronon.observability.TileDriftSeries
77
import ai.chronon.observability.TileSummarySeries
@@ -10,7 +10,6 @@ import org.scalatest.matchers.should.Matchers
1010

1111
import java.lang.{Double => JDouble}
1212
import java.lang.{Long => JLong}
13-
import scala.jdk.CollectionConverters.asScalaBufferConverter
1413

1514
class TileSeriesSerializationTest extends AnyFlatSpec with Matchers {
1615

@@ -40,7 +39,7 @@ class TileSeriesSerializationTest extends AnyFlatSpec with Matchers {
4039
val series =
4140
ThriftJsonCodec.fromJsonStr[TileDriftSeries](json, true, classOf[TileDriftSeries])(manifest[TileDriftSeries])
4241

43-
val drifts = series.getPercentileDriftSeries.asScala.toList
42+
val drifts = series.getPercentileDriftSeries.toScala
4443
drifts.size should be(5)
4544
drifts(0) should be(0.1)
4645
drifts(1) should be(Constants.magicNullDouble)
@@ -70,7 +69,7 @@ class TileSeriesSerializationTest extends AnyFlatSpec with Matchers {
7069
val series = ThriftJsonCodec.fromJsonStr[TileSummarySeries](json, true, classOf[TileSummarySeries])(
7170
manifest[TileSummarySeries])
7271

73-
val counts = series.getCount.asScala.toList
72+
val counts = series.getCount.toScala
7473
counts.size should be(5)
7574
counts(0) should be(100L)
7675
counts(1) should be(Constants.magicNullLong)

cloud_aws/BUILD.bazel

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
scala_library(
22
name = "cloud_aws_lib",
33
srcs = glob(["src/main/**/*.scala"]),
4+
format = select({
5+
"//tools/config:scala_2_13": False, # Disable for 2.13
6+
"//conditions:default": True, # Enable for other versions
7+
}),
48
visibility = ["//visibility:public"],
5-
format = True,
69
deps = [
710
maven_artifact("software.amazon.awssdk:dynamodb"),
811
maven_artifact("software.amazon.awssdk:regions"),
@@ -12,10 +15,10 @@ scala_library(
1215
maven_artifact("com.google.guava:guava"),
1316
maven_artifact("org.slf4j:slf4j-api"),
1417
maven_scala_artifact("org.scala-lang.modules:scala-collection-compat"),
15-
"//spark:lib",
16-
"//online:lib",
1718
"//api:lib",
1819
"//api:thrift_java",
20+
"//online:lib",
21+
"//spark:lib",
1922
],
2023
)
2124

@@ -37,12 +40,14 @@ test_deps = [
3740
scala_library(
3841
name = "test_lib",
3942
srcs = glob(["src/test/**/*.scala"]),
40-
format = True,
43+
format = select({
44+
"//tools/config:scala_2_13": False, # Disable for 2.13
45+
"//conditions:default": True, # Enable for other versions
46+
}),
4147
visibility = ["//visibility:public"],
4248
deps = test_deps,
4349
)
4450

45-
4651
scala_test_suite(
4752
name = "tests",
4853
srcs = glob(["src/test/**/*.scala"]),

cloud_aws/src/main/scala/ai/chronon/integrations/aws/DynamoDBKVStoreImpl.scala

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ai.chronon.integrations.aws
22

33
import ai.chronon.api.Constants
4+
import ai.chronon.api.ScalaJavaConversions._
45
import ai.chronon.online.KVStore
56
import ai.chronon.online.KVStore.GetResponse
67
import ai.chronon.online.KVStore.ListRequest
@@ -33,10 +34,11 @@ import java.time.Instant
3334
import java.util
3435
import java.util.concurrent.ConcurrentHashMap
3536
import scala.concurrent.Future
36-
import scala.jdk.CollectionConverters._
3737
import scala.util.Success
3838
import scala.util.Try
3939

40+
import scala.collection.Seq
41+
4042
object DynamoDBKVStoreConstants {
4143
// Read capacity units to configure DynamoDB table with
4244
val readCapacityUnits = "read-capacity"
@@ -97,8 +99,8 @@ class DynamoDBKVStoreImpl(dynamoDbClient: DynamoDbClient) extends KVStore {
9799

98100
val request =
99101
CreateTableRequest.builder
100-
.attributeDefinitions(keyAttributes.toList.asJava)
101-
.keySchema(keySchema.toList.asJava)
102+
.attributeDefinitions(keyAttributes.toList.toJava)
103+
.keySchema(keySchema.toList.toJava)
102104
.provisionedThroughput(ProvisionedThroughput.builder.readCapacityUnits(rcu).writeCapacityUnits(wcu).build)
103105
.tableName(dataset)
104106
.build
@@ -130,7 +132,7 @@ class DynamoDBKVStoreImpl(dynamoDbClient: DynamoDbClient) extends KVStore {
130132
val (getLookups, queryLookups) = requests.partition(r => r.startTsMillis.isEmpty)
131133
val getItemRequestPairs = getLookups.map { req =>
132134
val keyAttributeMap = primaryKeyMap(req.keyBytes)
133-
(req, GetItemRequest.builder.key(keyAttributeMap.asJava).tableName(req.dataset).build)
135+
(req, GetItemRequest.builder.key(keyAttributeMap.toJava).tableName(req.dataset).build)
134136
}
135137

136138
val queryRequestPairs = queryLookups.map { req =>
@@ -149,7 +151,7 @@ class DynamoDBKVStoreImpl(dynamoDbClient: DynamoDbClient) extends KVStore {
149151
dynamoDbClient.getItem(getItemReq).item()
150152
}
151153

152-
val response = item.map(i => List(i).asJava)
154+
val response = item.map(i => List(i).toJava)
153155
val resultValue: Try[Seq[TimedValue]] = extractTimedValues(response, defaultTimestamp)
154156
GetResponse(req, resultValue)
155157
}
@@ -183,7 +185,7 @@ class DynamoDBKVStoreImpl(dynamoDbClient: DynamoDbClient) extends KVStore {
183185

184186
val scanBuilder = ScanRequest.builder.tableName(request.dataset).limit(listLimit)
185187
val scanRequest = maybeExclusiveStartKeyAttribute match {
186-
case Some(value) => scanBuilder.exclusiveStartKey(Map(partitionKeyColumn -> value).asJava).build
188+
case Some(value) => scanBuilder.exclusiveStartKey(Map(partitionKeyColumn -> value).toJava).build
187189
case _ => scanBuilder.build
188190
}
189191

@@ -195,7 +197,7 @@ class DynamoDBKVStoreImpl(dynamoDbClient: DynamoDbClient) extends KVStore {
195197
val noPagesLeftResponse = ListResponse(request, resultElements, Map.empty)
196198
val listResponse = tryScanResponse match {
197199
case Success(scanResponse) if scanResponse.hasLastEvaluatedKey =>
198-
val lastEvalKey = scanResponse.lastEvaluatedKey().asScala.get(partitionKeyColumn)
200+
val lastEvalKey = scanResponse.lastEvaluatedKey().toScala.get(partitionKeyColumn)
199201
lastEvalKey match {
200202
case Some(av) => ListResponse(request, resultElements, Map(continuationKey -> av.b().asByteArray()))
201203
case _ => noPagesLeftResponse
@@ -218,7 +220,7 @@ class DynamoDBKVStoreImpl(dynamoDbClient: DynamoDbClient) extends KVStore {
218220
req.tsMillis.map(ts => Map(sortKeyColumn -> AttributeValue.builder.n(ts.toString).build)).getOrElse(Map.empty)
219221

220222
val putItemReq =
221-
PutItemRequest.builder.tableName(req.dataset).item((attributeMap ++ tsMap).asJava).build()
223+
PutItemRequest.builder.tableName(req.dataset).item((attributeMap ++ tsMap).toJava).build()
222224
(req.dataset, putItemReq)
223225
}
224226

@@ -272,8 +274,8 @@ class DynamoDBKVStoreImpl(dynamoDbClient: DynamoDbClient) extends KVStore {
272274
private def extractTimedValues(response: Try[util.List[util.Map[String, AttributeValue]]],
273275
defaultTimestamp: Long): Try[Seq[TimedValue]] = {
274276
response.map { ddbResponseList =>
275-
ddbResponseList.asScala.map { ddbResponseMap =>
276-
val responseMap = ddbResponseMap.asScala
277+
ddbResponseList.toScala.map { ddbResponseMap =>
278+
val responseMap = ddbResponseMap.toScala
277279
if (responseMap.isEmpty)
278280
throw new Exception("Empty response returned from DynamoDB")
279281

@@ -290,8 +292,8 @@ class DynamoDBKVStoreImpl(dynamoDbClient: DynamoDbClient) extends KVStore {
290292
private def extractListValues(tryScanResponse: Try[ScanResponse]): Try[Seq[ListValue]] = {
291293
tryScanResponse.map { response =>
292294
val ddbResponseList = response.items()
293-
ddbResponseList.asScala.map { ddbResponseMap =>
294-
val responseMap = ddbResponseMap.asScala
295+
ddbResponseList.toScala.map { ddbResponseMap =>
296+
val responseMap = ddbResponseMap.toScala
295297
if (responseMap.isEmpty)
296298
throw new Exception("Empty response returned from DynamoDB")
297299

@@ -333,8 +335,8 @@ class DynamoDBKVStoreImpl(dynamoDbClient: DynamoDbClient) extends KVStore {
333335
QueryRequest.builder
334336
.tableName(request.dataset)
335337
.keyConditionExpression(s"$partitionAlias = :partitionKeyValue AND $timeAlias BETWEEN :start AND :end")
336-
.expressionAttributeNames(attrNameAliasMap.asJava)
337-
.expressionAttributeValues(attrValuesMap.asJava)
338+
.expressionAttributeNames(attrNameAliasMap.toJava)
339+
.expressionAttributeValues(attrValuesMap.toJava)
338340
.build
339341
}
340342
}

0 commit comments

Comments
 (0)