Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to 2.13.0 #248

Closed
wants to merge 15 commits into from
Closed

Conversation

martijnhoekstra
Copy link
Contributor

@martijnhoekstra martijnhoekstra commented Apr 15, 2019

Aim for (but not always accomplish) source compatibility

Prefer to use "default" Seq over scala.collection.Seq

Problem

Explain the context and why you're making that change. What is the
problem you're trying to solve? In some cases there is not a problem
and this can be thought of being the motivation for your change.

Solution

Align with 2.13.0 collections

Result

Building under 2.13.0

@martijnhoekstra martijnhoekstra changed the title update to 2.13.x WIP update to 2.13.x Apr 15, 2019
@@ -30,7 +31,7 @@ private[util] class BatchExecutor[In, Out](
sizeThreshold: Int,
timeThreshold: Duration = Duration.Top,
sizePercentile: => Float = 1.0f,
f: Seq[In] => Future[Seq[Out]]
f: Iterable[In] => Future[Seq[Out]]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This raises, rather than lowers the requirements. Still, this seems reasonable in the name of unbenchmarked performance.

@martijnhoekstra
Copy link
Contributor Author

More errors than I thought on different cross-versions. Closing for now, until there is something closer.

* builder. A value containing the current version of the collection
* is notified for each incoming event.
*/
def build[U >: T, That <: Seq[U]](implicit factory: Factory[U, That]): Event[That] = buildAny(factory)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a seq-specific one, so that build that picks up any ambient factory/cbf builds at least a Seq. If we don't do this, on 2.13.0-RC1, we'll pick up a Factory[T, Array[T]] from the companion object of Factory, and build an Event[Array[T]] which is not a subtype of Event[Seq[T]]

def register(s: Witness[That]): Closable = {
val b = cbf()
val b = factory.newBuilder
Copy link
Contributor Author

@martijnhoekstra martijnhoekstra Apr 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The contract of builder makes calling result() more than once undefined behaviour (with the exception of ReusableBuilder which can call result() again after calling clear, but we're not clearing here anyway). That means that this relies upon (and always has relied upon) builder being well-behaved in cases where it doesn't have to play that nice according to its contract.

build.sbt Outdated Show resolved Hide resolved
build.sbt Outdated Show resolved Hide resolved
project/plugins.sbt Outdated Show resolved Hide resolved
browseDir(f, loader, "", buf)
else
browseJar(f, loader, buf, history)
if (!history.contains(uri)) {
Copy link
Contributor Author

@martijnhoekstra martijnhoekstra Apr 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done URI checking local to this function -- forking in test resulted in duplicate CpInfo's in the result

@@ -157,8 +157,8 @@ object Reader {
require(chunkSize > 0, s"chunkSize should be > 0 but was $chunkSize")

@tailrec
private def loop(acc: Seq[Buf], in: Buf): Seq[Buf] = {
if (in.length < chunkSize) acc :+ in
private def loop(acc: ListBuffer[Buf], in: Buf): Seq[Buf] = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't getting any prettier. I'd be happy to replace with a non-mutable loop, but went for minimal invasiveness


test("close while read pending") {
def closeWhileReadPending = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of these had to be factored out, for reasons that aren't clear to me, and may be bugs in assert or scalac: it seems to have to do with macros re-evaluating/re-capturing variables that trip up scalac.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reported at scala/bug#11556

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran into issues with PipeTest also in my previous minor experiment, subscribed to the bug.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Factoring the test body into a method consistently worked around for now, if you run into it elsewhere.

.toSeq
.sortWith(_._1 < _._1)
.map { case (k, v) => BucketAndCount(k, k + 1, v) }
.sortBy(_.lowerLimit)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simplified after eliminating the deprecated mapValues

@codecov-io
Copy link

codecov-io commented Jun 3, 2019

Codecov Report

Merging #248 into develop will increase coverage by 0.29%.
The diff coverage is 78.22%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #248      +/-   ##
===========================================
+ Coverage    46.68%   46.97%   +0.29%     
===========================================
  Files          231      229       -2     
  Lines        14848    14752      -96     
  Branches      1187      918     -269     
===========================================
- Hits          6932     6930       -2     
+ Misses        7916     7822      -94
Impacted Files Coverage Δ
...re/src/main/scala/com/twitter/util/Awaitable.scala 96.29% <ø> (ø) ⬆️
...l-core/src/main/scala/com/twitter/util/Timer.scala 77.11% <ø> (ø) ⬆️
...ore/src/main/scala/com/twitter/util/Closable.scala 71.66% <ø> (ø) ⬆️
.../src/main/scala/com/twitter/concurrent/Offer.scala 88.23% <ø> (ø) ⬆️
...e/src/main/scala/com/twitter/util/FuturePool.scala 78% <ø> (ø) ⬆️
util-jvm/src/main/scala/com/twitter/jvm/Jvm.scala 90.9% <ø> (ø) ⬆️
...cala/com/twitter/finagle/stats/StatsReceiver.scala 70% <0%> (-17.5%) ⬇️
.../scala/com/twitter/concurrent/OfferBenchmark.scala 0% <0%> (ø) ⬆️
...com/twitter/zk/coordination/ShardCoordinator.scala 0% <0%> (ø) ⬆️
.../src/main/scala/com/twitter/util/StorageUnit.scala 86.2% <0%> (ø) ⬆️
... and 39 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0d77572...d45a68b. Read the comment docs.

@@ -253,4 +256,22 @@ trait StatsReceiver {
}
}

abstract class AbstractStatsReceiver extends StatsReceiver
abstract class AbstractStatsReceiver extends StatsReceiver {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to make overriding varargs from Java source compatible between 2.12.x and 2.13.x, made the varargs methods final, and made a non-varargs non-aliased abstract method with a scala.collection.Seq argument as an extension point for overriding.

@martijnhoekstra martijnhoekstra changed the title WIP update to 2.13.x Update to 2.13.x (targets 2.13.0-RC3) Jun 4, 2019
.travis.yml Outdated Show resolved Hide resolved
@martijnhoekstra martijnhoekstra changed the title Update to 2.13.x (targets 2.13.0-RC3) Update to 2.13.0 Jun 13, 2019
@yufangong
Copy link
Contributor

That benchmark run will be complete in 10.5 days.

I can't spare hardware to chug on this for that amount of time. I'll try to isolate the likeliest things to have regressed, and run limited runs on those, but that's going to be very limited.

Can you see if hardware can be allocated from your side to chug through the bench suite for all benchmarks for all 5 configurations?

Yes, I am pulling in this branch and start working on the internal build as well as benchmarks.

@martijnhoekstra
Copy link
Contributor Author

martijnhoekstra commented Jun 25, 2019 via email

@yufangong
Copy link
Contributor

Updates:
We are able to make pants work with this branch yay! Currently, there are some upstream code failures against this, but it is not very bad, and we are working on resolving those.

We will run a few more benchmarks in addition to the ones you have run. Thanks a ton for doing this!
Do you have any updates to the AsyncStreamBenchmark and CumulativeGaugeBenchmark?

@yufangong
Copy link
Contributor

Interpreted from https://gist.github.com/martijnhoekstra/3303264b113a430cf762b7d3d3ab0d4c:

util-benchmarks:

concurrent

  • AsyncQueueBenchmark - done
    alternatePollThenOffer (may because this benchmark is not reliable)
  • AsyncSemaphoreBenchmark - done - green results
  • AsyncStreamBenchmark - done
  • BrokerBenchmark
  • ConduitSpscBenchmark
  • OfferBenchmark
  • OnceBenchmark
  • SchedulerBenchmark

finagle

  • CumulativeGaugeBenchmark - done
  • StatBenchmark

hashing

  • HashableBench
  • KetamaDistributorBench
  • KeyHasherBenchmark

io

  • BufBenchmark
  • BufConcatBenchmark
  • PipeBenchmark

jvm

  • CpuProfileBenchmark

string

  • StringConcatenationBenchmark

util

  • DurationBenchmark
  • FutureBenchmark (CollectToTry optimized) - done - green result
  • LocalBenchmark
  • MonitorBenchmark
  • PromiseBenchmark
  • StdBenchAnnotations
  • StopwatchBenchmark
  • TimeBenchmark
  • TimerBenchmark
  • TryBenchmark

@martijnhoekstra
Copy link
Contributor Author

martijnhoekstra commented Jun 27, 2019

The Cumulative Gauge benchmarks were looking good now. The 3 AsyncStream ones had too much jitter in the run I did to be of value. Can you include them on your run?

Note that the benchmarks I ran in many cases didn't have the runtime nor the number of warmup and measurement iterations recommended in the tests, simply for the sake of not taking days. I basically cut every corner that could be cut without crashing or burning. I would still have expected regressions to at least show up, but possibly evaporate under more scrutiny, but more principled runs for everything are welcome.

Also, I left 2.11 out of the benchmarks and treated 2.12 pre PR as a baseline to measure 2.12 post PR and 2.13 post PR against. That may actually be an acceptable corner to cut.

@@ -1404,7 +1422,7 @@ def join[%s](%s): Future[(%s)] = join(Seq(%s)).map { _ => (%s) }""".format(
sizeThreshold: Int,
timeThreshold: Duration = Duration.Top,
sizePercentile: => Float = 1.0f
)(f: Seq[In] => Future[Seq[Out]]
)(f: Iterable[In] => Future[Seq[Out]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change, just want to make sure this is necessary. If so we should add an entry to CHANGELOG(i can add it on my branch).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's better to keep this at scala.collection.Seq then.

  • For 2.13 it may be a breaking change, but some refactoring is already expected there.
  • For 2.12.x it won't break anything.

I will investigate, and come back to it this Tuesday, as I'm heading into a long weekend.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

b5b124b is a way to do it without the Iterable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests seem to be mocking me over that change.

I've never used any mocking frameworks. What's the correct way to fix this? Include function composition in the mock?

Copy link
Contributor

@yufangong yufangong Jul 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @martijnhoekstra, I modified tests to mock the composed method, and verify against m.

          val m = mock[Any => Future[Seq[Int]]]
          val f = mock[scala.collection.Seq[Int] => Future[Seq[Int]]]
          when(f.compose(any())).thenReturn(m)

I have your branch internally almost ready, passed all tests but only running the rest of benchmarks.

@@ -126,7 +126,7 @@ class StorageUnit(val bytes: Long) extends Ordered[StorageUnit] {
def max(other: StorageUnit): StorageUnit =
if (this > other) this else other

override def toString(): String = inBytes + ".bytes"
override def toString(): String = s"$inBytes bytes"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to revert this for some utility usage.

Copy link
Contributor Author

@martijnhoekstra martijnhoekstra Jul 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the format, or for the interpolator? I overlooked the original ., that change was unintentional, but the interpolator is there because any2stringadd is on its way out, and should be either s"$inBytes.bytes" with, or inBytes.toString + ".bytes" without interpolator

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gotcha, I will only add the . then.

final override def stat(verbosity: Verbosity, name: String*): Stat = statImpl(verbosity, name)
protected def statImpl(verbosity: Verbosity, name: scala.collection.Seq[String]): Stat

final override def addGauge(name: String*)(f: => Float): Gauge = addGauge(Verbosity.Default, name: _*)(f)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add @varargs for these two addGauge methods, right?

Copy link
Contributor Author

@martijnhoekstra martijnhoekstra Jul 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@varargs makes little sense if the last argument list isn't the varargs list. Shall I check if they actually generate any different bytecode?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does produce different bytecode.

The code

object Test {
  @varargs def withAnnotation(args: Int*)(extra: String) = args.length
}

produces extra an extra forwarder method:

  public int withAnnotation(int..., java.lang.String);
    descriptor: ([ILjava/lang/String;)I
    flags: ACC_PUBLIC, ACC_VARARGS
    Code:
      stack=3, locals=3, args_size=3
         0: aload_0
         1: getstatic     #31                 // Field scala/runtime/ScalaRunTime$.MODULE$:Lscala/runtime/ScalaRunTime$;
         4: aload_1
         5: invokevirtual #35                 // Method scala/runtime/ScalaRunTime$.wrapIntArray:([I)Lscala/collection/immutable/ArraySeq;
         8: aload_2
         9: invokevirtual #38                 // Method withAnnotation:(Lscala/collection/immutable/Seq;Ljava/lang/String;)I
        12: ireturn

But this can't ever be called with varargs from Java, since while this is valid JVM bytecode, there is no equivalent Java code, where the varargs must always be last.

In fact, I'd argue that the annotation here can't be useful, and should be deprecated in scala

@@ -12,17 +12,17 @@ val zkDependency = "org.apache.zookeeper" % "zookeeper" % zkVersion excludeAll(
ExclusionRule("javax.jms", "jms")
)
val slf4jVersion = "1.7.21"
val jacksonVersion = "2.9.8"
val jacksonVersion = "2.9.9"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a necessary change along with this diff? or we can bump up the version separately?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no 2.9.8 for 2.13.0, so we have to bump it independently, we have to bump it before and rebase this on top of that.

I have a branch where that's done --develop...martijnhoekstra:pre213 is a diff of a reasonable bump of dependencies and elimination of deprecations that go along with that. But to be honest, I'm not sure what value that brings, other than better baseline for benchmarking maybe? I doubt it would matter much.

@martijnhoekstra
Copy link
Contributor Author

@yufangong Do you have an update how benchmarks are going? Still ongoing? Are there already any provisional "red" results I can start working on?

@yufangong
Copy link
Contributor

I am only running against 2.12, no obvious red flag so far
LocalBenchmark seems improved.
I will keep update results here:
https://gist.github.com/yufangong/61eeac9bc0765e913985e743c5300028

@martijnhoekstra
Copy link
Contributor Author

martijnhoekstra commented Jul 18, 2019

@yufangong I snuck sbt 1.3.0 RC3 in in 85fb0fd because RC2 has some annoying bugs.

I'm actually not too sure if sbt 1.3 should be part of this PR at all, or be factored into a different PR entirely (when 1.3.0 is fully released).

What's the status on your side?

@yufangong
Copy link
Contributor

@martijnhoekstra agreed that we should make it against a released version, I changed it to 1.2.8 and it seems working. Apologies that I was not actively working on this past week but going to get back on this branch next week.

@martijnhoekstra
Copy link
Contributor Author

@yufangong what's the current status on benchmarking and review? Is this good to go if the benches are good?

@yufangong
Copy link
Contributor

@yufangong what's the current status on benchmarking and review? Is this good to go if the benches are good?

We have just upgraded Jackson version in source as part of the util cross-build effort. The most recent benchmark BrokerBenchmark seems a bit regression. I'll run again to see if thats valid.
I will move some questions in the review I cannot answer here. I will let you know if we need to take any actions, but so far it's going good.

@@ -313,7 +313,7 @@ trait App extends Closable with CloseAwaitably {
deadline: Time
): Future[Unit] = {
Future
.collectToTry(lastExits.asScala.toSeq.map(_.close(deadline)))
.collectToTry(lastExits.asScala.map(_.close(deadline)).toSeq)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does it matter where the toSeq is?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ping

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was part of a refactor that turned out wasn't needed, and this is a vestigial remainder of that refactor.

In other words, I don't think it matters, and pushed a commit to undo it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for clarifying.

@yufangong
Copy link
Contributor

benchmarks are all clear, feel free to take a look at the result see if anything jumps out
https://gist.github.com/yufangong/61eeac9bc0765e913985e743c5300028

could/should be squased onto 1af964a
@yufangong
Copy link
Contributor

yufangong commented Aug 7, 2019

@martijnhoekstra It is merged here d5d20cc! Yay! Thanks for your patience! Can you share us your strategy of this migration that we can pass to finagle/finatra/etc as well as the internal source repo? Are there any automation tool or migration tool used and helpful? Thanks again!

@martijnhoekstra
Copy link
Contributor Author

This migration, I did with elbow grease: set the scala version, and fix all deprecation warnings and compiler errors.

For other repos, I'd make sure that you update all dependencies to a version that cross-builds, fix all deprecation warnings, then run the scalafix at https://github.com/scala/scala-collection-compat#collection213crosscompat, and take that as a starting point -- does the generated code make sense, or does it need an extra layer of shine. For example, in this repo, in many cases an ArrayBuffer was used to build a collection and return it as a (mutable) Seq. You can call toSeq at the end and call it good, but better is to use a Builder and return it's built results, which in many cases is O(1) rather than the O(n) that toSeq on the buffer would be. I don't think more automated fixes for that are feasible.

I intend to work my way towards Chill on 2.13 migration.

@yufangong
Copy link
Contributor

@martijnhoekstra Thanks so much! Closing this PR as resolved.

@yufangong yufangong closed this Aug 7, 2019
@SethTisue
Copy link
Contributor

SethTisue commented Aug 9, 2019

ticket about publishing for 2.13: #253

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

6 participants