netflixoss season 2 episode 2 - reactive / async
Embed Size (px)
DESCRIPTION
NetflixOSS Season 2 Episode 2 Meetup, Reactive/Async theme. Lightning talks by Netflix engineers, as well as guest speakers from Square, Couchbase and Typesafe.TRANSCRIPT

Season 2 Episode 2July 9, 2014

Evening Outline
Lightning Talks:● Reactive / Rx● RxJava● RxNetty● Ribbon 2.0● Karyon 2.0
Guest Speakers● Jake Wharton, Square● Matt Ingenthron, Couchbase● Will Sargent, Typesafe

Netflix Lightning talks






Composable Functions
Reactively Applied

Composable Functions
Reactively Applied

Composable Functions
Reactively Applied

Clojure Scala
Groovy JRuby
Java 8
(->
(Observable/from ["one" "two" "three"])
(.take 2)
(.subscribe (rx/action [arg] (println arg))))
Observable("one", "two", "three")
.take(2)
.subscribe((arg: String) => {
println(arg)
})
Observable.from("one", "two", "three")
.take(2)
.subscribe(lambda { |arg| puts arg })
Observable.from("one", "two", "three")
.take(2)
.subscribe({arg -> println(arg)})
Observable.from("one", "two", "three")
.take(2)
.subscribe(System.out::println);

return new UserCommand(userId).observe().flatMap(user -> {
Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe()
.flatMap(catalogList -> {
return catalogList.videos().<Map<String, Object>> flatMap(video -> {
Observable<Bookmark> bookmark = new BookmarkCommand(video).observe();
Observable<Rating> rating = new RatingsCommand(video).observe();
Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe();
return Observable.zip(bookmark, rating, metadata, (b, r, m) -> {
return combineVideoData(video, b, r, m);
});
});
});
Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> {
return s.getDataAsMap();
});
return Observable.merge(catalog, social);
}).flatMap(data -> {
return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER);
});

return new UserCommand(userId).observe().flatMap(user -> {
Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe()
.flatMap(catalogList -> {
return catalogList.videos().<Map<String, Object>> flatMap(video -> {
Observable<Bookmark> bookmark = new BookmarkCommand(video).observe();
Observable<Rating> rating = new RatingsCommand(video).observe();
Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe();
return Observable.zip(bookmark, rating, metadata, (b, r, m) -> {
return combineVideoData(video, b, r, m);
});
});
});
Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> {
return s.getDataAsMap();
});
return Observable.merge(catalog, social);
}).flatMap(data -> {
return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER);
});

return new UserCommand(userId).observe().flatMap(user -> {
Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe()
.flatMap(catalogList -> {
return catalogList.videos().<Map<String, Object>> flatMap(video -> {
Observable<Bookmark> bookmark = new BookmarkCommand(video).observe();
Observable<Rating> rating = new RatingsCommand(video).observe();
Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe();
return Observable.zip(bookmark, rating, metadata, (b, r, m) -> {
return combineVideoData(video, b, r, m);
});
});
});
Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> {
return s.getDataAsMap();
});
return Observable.merge(catalog, social);
}).flatMap(data -> {
return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER);
});

return new UserCommand(userId).observe().flatMap(user -> {
Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe()
.flatMap(catalogList -> {
return catalogList.videos().<Map<String, Object>> flatMap(video -> {
Observable<Bookmark> bookmark = new BookmarkCommand(video).observe();
Observable<Rating> rating = new RatingsCommand(video).observe();
Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe();
return Observable.zip(bookmark, rating, metadata, (b, r, m) -> {
return combineVideoData(video, b, r, m);
});
});
});
Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> {
return s.getDataAsMap();
});
return Observable.merge(catalog, social);
}).flatMap(data -> {
return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER);
});

return new UserCommand(userId).observe().flatMap(user -> {
Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe()
.flatMap(catalogList -> {
return catalogList.videos().<Map<String, Object>> flatMap(video -> {
Observable<Bookmark> bookmark = new BookmarkCommand(video).observe();
Observable<Rating> rating = new RatingsCommand(video).observe();
Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe();
return Observable.zip(bookmark, rating, metadata, (b, r, m) -> {
return combineVideoData(video, b, r, m);
});
});
});
Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> {
return s.getDataAsMap();
});
return Observable.merge(catalog, social);
}).flatMap(data -> {
return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER);
});

return new UserCommand(userId).observe().flatMap(user -> {
Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe()
.flatMap(catalogList -> {
return catalogList.videos().<Map<String, Object>> flatMap(video -> {
Observable<Bookmark> bookmark = new BookmarkCommand(video).observe();
Observable<Rating> rating = new RatingsCommand(video).observe();
Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe();
return Observable.zip(bookmark, rating, metadata, (b, r, m) -> {
return combineVideoData(video, b, r, m);
});
});
});
Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> {
return s.getDataAsMap();
});
return Observable.merge(catalog, social);
}).flatMap(data -> {
return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER);
});

return new UserCommand(userId).observe().flatMap(user -> {
Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe()
.flatMap(catalogList -> {
return catalogList.videos().<Map<String, Object>> flatMap(video -> {
Observable<Bookmark> bookmark = new BookmarkCommand(video).observe();
Observable<Rating> rating = new RatingsCommand(video).observe();
Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe();
return Observable.zip(bookmark, rating, metadata, (b, r, m) -> {
return combineVideoData(video, b, r, m);
});
});
});
Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> {
return s.getDataAsMap();
});
return Observable.merge(catalog, social);
}).flatMap(data -> {
return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER);
});

return new UserCommand(userId).observe().flatMap(user -> {
Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe()
.flatMap(catalogList -> {
return catalogList.videos().<Map<String, Object>> flatMap(video -> {
Observable<Bookmark> bookmark = new BookmarkCommand(video).observe();
Observable<Rating> rating = new RatingsCommand(video).observe();
Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe();
return Observable.zip(bookmark, rating, metadata, (b, r, m) -> {
return combineVideoData(video, b, r, m);
});
});
});
Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> {
return s.getDataAsMap();
});
return Observable.merge(catalog, social);
}).flatMap(data -> {
return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER);
});

+

1.3.x (current production)1.4 Release Candidate (available now) - support for fully non-blocking1.4.0 Final (coming months)

0.19.x (current production)0.20 Release Candidates (coming weeks) - the “backpressure” release0.20.0 Final1.0 Release Candidates (Summer 2014) - no more breaking changes1.0.0 Final (Summer/Fall 2014)

github.com/Netflix/RxJava github.com/ReactiveX/RxJava
Version 1.x to be releasedon Maven Central with groupId io.reactivex.*

github.com/Netflix/RxJava github.com/ReactiveX/RxJava
github.com/ReactiveX/RxScalagithub.com/ReactiveX/RxGroovygithub.com/ReactiveX/RxClojuregithub.com/ReactiveX/RxKotlingithub.com/ReactiveX/RxJRubygithub.com/ReactiveX/RxAndroidetc ...
Version 1.x to be releasedon Maven Central with groupId io.reactivex.*

Along the way we started thinking about non-blocking IO ...

WSPerfLabhttps://github.com/Netflix-Skunkworks/WSPerfLab

WSPerfLabhttps://github.com/Netflix-Skunkworks/WSPerfLab
Best case latency for response is ~155ms


RxNetty Performance
client:wrk -t 4 -c 400
server CPUs~2% idle
NOTE: It is still early days for RxNetty tuning; more to come

Tuning for an acceptable latency distribution
client: ab
server CPUusage varieswith load

RxNetty vs TomcatEvent loop model more efficient than thread pools:● Reduces thread CPU overheads● Reduced thread lock contention● Better work scheduling: platform chooses, instead of the
kernel scheduling threads● Warmer CPU caches and memory locality on NUMA:
event worker threads can have better CPU affinity

RxNetty Micro BenchmarkingServer: AWS c3.xlarge, tuned to reduce perturbationsClient: Multi-threaded (wrk -t 4 -c 100/400), 1 min warmup● Sufficient load to exhaust CPU capacity.Active Benchmarking Methodology:● Tools: sysstat, perf_events, SystemTap, flame graphs, …● USE Method, static performance tuning, etc.● Target analyzed during benchmark confirm target results
and identify (and tune) true limiters.

Approaching light speed...

Hello World● All platforms are <100us for Hello World, so their CPU
overheads are already negligible for this workload.○ Consider application compute times of >1ms
● Hence testing “Hello Netflix” as well: a basic Netflix service Hello World with dependency requests.
● Although, Hello World max latency (not pictured) was still much higher for Tomcat...

Visualizing Tomcat Overheads● Flame graph of
Java CPU timefor Hello Worldbenchmark
● Thread and I/Omanagementoverheads

Visualizing RxNetty Overheads● Flame graph of Java CPU
time for Hello World● Time in read() and write()
(doing I/O; ie, doing “work”)dominates
● Flame graphs also studiedof kernel and user-time
● Many improvements foundand fixed

Blog post forthcoming… Keywords:● Oracle JDK 1.8.0 (others tried); Xmx & Xms; different
GCs and settings, eg -XX:+UseConcMarkSweepGC● Apache-tomcat 7.0.54: tested BIO, NIO, APR;
maxThreads=150 or 512; maxQueueSize=up to 400; acceptCount=up to 100; disabled access log, ...
● Node.js 0.6.12 with clustering (faster than 0.10/0.11)● vert.x 2.1 with -instances 4● Java profilers: Google lightweight java profiler; JFR; …● perf_events; SystemTap; flame graphs, ...

RxNetty & Karyon 2.0
@NiteshKant

Netflix IPC Stack (1.0)Apache
HTTP Client
Eureka (Service Registry)
Server (Karyon)
Apache
Tomcat
Client
Hystrix
EVCache
Ribbon
Load Balancing
Eureka Integration
Metrics (Servo)
Bootstrapping (Governator)
Metrics (Servo)
Admin ConsoleHTTP
Eureka Integration
Registration
Fetch Registry
A Blocking Architecture

Netflix IPC Stack (2.0)Client (Ribbon 2.0)
Eureka (Service Registry)
Server (Karyon)
Ribbon Transport
Load Balancing
Eureka Integration
Metrics (Servo)
Bootstrapping (Governator)
Metrics (Servo)
Admin Console
HTTP
Eureka Integration
Registration
Fetch Registry
Ribbon
Hystrix
EVCache
RxNetty
RxNetty
UDP
TCPWebSockets
SSE
A Completely Reactive
Architecture

“We want an extremely performant IPC library”
Heard this before?

Reinventing The Wheel?
Image Courtesy: Oxford Creativity
Finagle
Vert.x
Ratpack
Tons of
Netty based
Frameworks?

On the lookout …..
● Low level control.○ Traffic Shaping. (Zuul)○ Backpressure.
● Netflix “fit”○ Reactive programming model.○ Netflix Components (Eureka, Archaius, Governator, etc.)

RxNetty
Reactive Extensions To Netty
Nucleus of Netflix’s IPC stack
Powering Ribbon 2.0 & Karyon 2.0
https://github.com/Netflix/RxNetty

Example (Server Sent Events)RxNetty.createHttpServer(8887, (request, response) -> { return Observable.interval(1, TimeUnit.SECONDS) .flatMap(interval -> { return response.writeAndFlush(new ServerSentEvent(...)); }); }, PipelineConfigurators.<ByteBuf>sseServerConfigurator()).start();
Server
Client
RxNetty.<ByteBuf, ServerSentEvent>createHttpClient("127.0.0.1", 8887, PipelineConfigurators.sseClientConfigurator()) .submit(HttpClientRequest.<ByteBuf>createGet("/")) .flatMap(response -> response.getContent()) .toBlocking().forEach(event -> System.out.println(event))

data: interval: 0
data: interval: 1
data: interval: 2
data: interval: 3
Output …...
data: interval: 4
data: interval: 5

Example (Stream Aggregation)
RxNetty.createHttpServer(8887, (request, response) -> { return Observable.interval(1, TimeUnit.SECONDS) .flatMap(interval -> { return response.writeAndFlush(new ServerSentEvent(...)); }); }, PipelineConfigurators.<ByteBuf>sseServerConfigurator()).start();{ } *3

Example (Stream Aggregation)
Observable.merge(RxNetty.<ByteBuf, ServerSentEvent>createHttpClient("127.0.0.1", 8887...),
RxNetty.<ByteBuf, ServerSentEvent>createHttpClient("127.0.0.1", 8888...),
RxNetty.<ByteBuf, ServerSentEvent>createHttpClient("127.0.0.1", 8889...))).toBlocking().forEach(event -> System.out.println(event))
RxNetty.createHttpServer(8887, ...).start(); RxNetty.createHttpServer(8888, ...).start(); RxNetty.createHttpServer(8889, ...).start();

data: interval (8887): 0
data: interval (8888): 0
data: interval (8887): 1
data: interval (8887): 2
Output …...
data: interval (8889): 0
data: interval (8888): 1

Karyon 2.0
● Adds Netflix constructs (governator, eureka, archaius, etc.) on RxNetty.
● Provides extensions (servlet, jersey, etc.) to RxNetty.
● More palatable to application developers.

Example @ArchaiusBootstrap
@KaryonBootstrap(name = "hello-netflix-oss")
@Modules(include = {HelloNossApp.KaryonJerseyModuleImpl.class, KaryonWebAdminModule.class, KaryonEurekaModule.class})
public final class HelloNossApp {
public static class KaryonJerseyModuleImpl extends KaryonJerseyModule {
protected void configure() {
super.configure();
bind(AuthenticationService.class).to(AuthenticationServiceImpl.class);
}
public int serverPort() { return 8888; }
public int shutdownPort() { return 8899; }
public void configureInterceptors(GovernatorHttpInterceptorSupport<ByteBuf, ByteBuf> interceptorSupport) {
interceptorSupport.forUri("/hello").interceptIn(AuthInterceptor.class);
}
}
}

Ribbon 2Allen Wang

● Tendency to become “fat” with boilerplate code
● Hystrix integration● Go “async”● “Declare” a client, rather than “write” a client
Ribbon 2.0 - Motivation

Ribbon 2.0: Let’s tie ‘em up

Ribbon 2.0 - New “ribbon” module● Template support for building HTTP request● Rx style non-blocking and blocking APIs● Hystrix built-in for fault tolerance ● Pluggable cache access with EVCache
implementation● Annotation based client creation as an
alternative

Ribbon 2.0 - Annotation Examplepublic interface MovieService {
RibbonRequest<Recommendations> recommendationsByUserId(@Var("userId") String userId);
}
@TemplateName(“recommendations”)
@Http(method = HttpMethod.GET, uriTemplate="/users/{userId}/recommendations")
@Hystrix(fallbackHandler = RecommendationFallbackHandler.class)
@EvCache(name = "movie-rec", appName = "movieService", cacheKeyTemplate = "{userId}")
MovieService movieService = Ribbon.from(MovieService.class);
Observable<Recommendations> result =
movieService.recommendationsByUserId(“user1”)
.toObservable();

Ribbon 2: Template ExampleHttpResourceGroup movieService = Ribbon.createHttpResourceGroup("movieService");HttpRequestTemplate<ByteBuf> template1 = movieService.newRequestTemplate("recommendationsByUserId", ByteBuf.class) .withMethod("GET") .withUriTemplate("/users/{userId}/recommendations") .withFallbackProvider(new RecommendationServiceFallbackHandler());
RibbonRequest<ByteBuf> request = template1.requestBuilder() .withRequestProperty("userId", “user1”).build();Observable<ByteBuf> result = request.toObservable(); // non blockingByteBuf result = request.execute(); // blocking

Ribbon 2.0 - Other New Features
● Ribbon’s async module - “ribbon-transport”○ HTTP, TCP and UDP clients on top of RxNetty with
load balancing capability● New load balancing features
○ Command pattern load balancing APIs implemented with Rx for easy integration for third party client
○ Shuffle sharding server list for fault isolation

Reactive outside Netflix

RetrofitJake Wharton

Retrofit
Example: GitHub API List Contributors
GET /repos/:owner/:repo/contributors

Retrofit
GET /repos/:owner/:repo/contributors
interface GitHub { List<Contributor> contributors( String owner, String repo);}
class Contributor { String login; long contributions;}

Retrofit
GET /repos/:owner/:repo/contributors
interface GitHub { @GET("/repos/{owner}/{repo}/contributors") List<Contributor> contributors( String owner, String repo);}

Retrofit
GET /repos/:owner/:repo/contributors
interface GitHub { @GET("/repos/{owner}/{repo}/contributors") List<Contributor> contributors( @Path("owner") String owner, @Path("repo") String repo);}

RetrofitRestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.github.com/") .build();
GitHub gitHub = restAdapter.create(GitHub.class);
List<Contributor> contributors = gitHub.contributors("netflix", "rxjava");

RetrofitList<Contributor> contributors = gitHub.contributors("netflix", "rxjava");
for (Contributor c : contributors) { println(c.login + '\t' + c.contributions);}
1483 benjchristensen225 zsxwing167 samuelgruetter146 jmhofer137 akarnokd105 DavidMGross102 AppliedDuality...

RetrofitRestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://drive.google.com/api/") .setConverter(new ProtoConverter()) .build();

Retrofitinterface GitHub { @GET("/repos/{owner}/{repo}/contributors") List<Contributor> contributors( @Path("owner") String owner, @Path("repo") String repo);}

Retrofitinterface GitHub { @GET("/repos/{owner}/{repo}/contributors?anon=true") List<Contributor> contributors( @Path("owner") String owner, @Path("repo") String repo);}

Retrofitinterface GitHub { @GET("/repos/{owner}/{repo}/contributors") List<Contributor> contributors( @Path("owner") String owner, @Path("repo") String repo, @Query("anon") boolean includeAnonymous);}

Retrofitinterface GitHub { @POST("/repos/{owner}/{repo}/hooks") Response createHook( @Path("owner") String owner, @Path("repo") String repo, @Body Hook hook);}
class Hook { String name; Map<String, Object> config; List<String> events; boolean active;}

RetrofitRestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://drive.google.com/api/") .setConverter(new ProtoConverter()) .setClient(new OkClient()) .build();

RetrofitOkHttpClient client = new OkHttpClient();client.setProtocols(Arrays.asList(HTTP_2));
RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://drive.google.com/api/") .setConverter(new ProtoConverter()) .setClient(new OkClient(client)) .build();

Retrofit● Form URL encoding● Response streaming● Request interceptors● Dynamic headers● Android
● Multipart● Logging● Mock RestAdapter● Asynchronous invoke● AppEngine
...and more!

Retrofit + RxJavainterface GitHub { @GET("/repos/{owner}/{repo}/contributors") List<Contributor> contributors( @Path("owner") String owner, @Path("repo") String repo);}

Retrofit + RxJavainterface GitHub { @GET("/repos/{owner}/{repo}/contributors") Observable<List<Contributor>> contributors( @Path("owner") String owner, @Path("repo") String repo);}

Retrofit + RxJavagitHub.contributors("netflix", "rxjava") .lift(flattenList()) .forEach(c -> println(c.login + '\t' + c.contributions));
1483 benjchristensen225 zsxwing167 samuelgruetter146 jmhofer137 akarnokd105 DavidMGross102 AppliedDuality...

Retrofit + RxJavainterface GitHub { @GET("/repos/{owner}/{repo}/contributors") Observable<List<Contributor>> contributors( @Path("owner") String owner, @Path("repo") String repo);
@GET("/users/{user}") Observable<User> user( @Path("user") String user);}

Retrofit + RxJavagitHub.contributors("netflix", "rxjava") .lift(flattenList()) .flatMap(c -> gitHub.user(c.login)) .forEach(user -> println(user.name));
Ben ChristensenShixiong ZhunullJoachim HofernullDavid Grossnull...

Retrofit + RxJavagitHub.contributors("netflix", "rxjava") .lift(flattenList()) .flatMap(c -> gitHub.user(c.login)) .filter(user -> user.name != null) .forEach(user -> println(user.name));
Ben ChristensenShixiong ZhuJoachim HoferDavid GrossMatthias KäpplerJustin RyanMairbek Khadikov...

Retrofit + RxJavainterface GitHub { @GET("/repos/{owner}/{repo}/contributors") Observable<List<Contributor>> contributors( @Path("owner") String owner, @Path("repo") String repo);
@GET("/users/{user}/starred") Observable<List<Repo>> starred( @Path("user") String user);}

Retrofit + RxJavagitHub.contributors("netflix", "rxjava") .lift(flattenList()) .flatMap(c -> gitHub.starred(c.login)) .lift(flattenList()) .groupBy(r -> r.full_name) .flatMap(g -> g.count().map(c -> c + "\t" + g.getKey())) .toSortedList((a, b) -> b.compareTo(a)) .lift(flattenList()) .take(8) .forEach(Main::println);

Retrofit + RxJava
7 Netflix/RxJava2 twitter/finagle2 scala/scala2 mbostock/d32 kpelykh/docker-java2 Netflix/zuul2 Netflix/feign2 Netflix/archaius

Retrofit + RxJavagitHub.contributors("square", "retrofit") .lift(flattenList()) .flatMap(c -> gitHub.starred(c.login)) .lift(flattenList()) .groupBy(r -> r.full_name) .flatMap(g -> g.count().map(c -> c + "\t" + g.getKey())) .toSortedList((a, b) -> b.compareTo(a)) .lift(flattenList()) .take(8) .forEach(Main::println);

gitHub.contributors("square", "retrofit") .lift(flattenList()) .flatMap(c -> gitHub.starred(c.login)) .lift(flattenList()) .filter(r -> !r.full_name.startsWith("square/")) .groupBy(r -> r.full_name) .flatMap(g -> g.count().map(c -> c + "\t" + g.getKey())) .toSortedList((a, b) -> b.compareTo(a)) .lift(flattenList()) .take(8) .forEach(Main::println);
Retrofit + RxJava

Retrofit + RxJava
4 frankiesardo/auto-parcel4 Comcast/FreeFlow3 xxv/android-lifecycle3 robolectric/robolectric3 inmite/android-butterknife-zelezny3 google/auto3 facebook/rebound3 etsy/AndroidStaggeredGrid3 Netflix/RxJava

Retrofitsquare.github.io/retrofit

CouchbaseMatt Ingenthron

Couchbase is...
Document-oriented (JSON) database ...Low latency & high throughput …Highly available and scalable ...

Motivations with Couchbase Java
➢ Simplify ...➢ Even *more* performance➢ Build for the JVM, not for the Java language
○ Have Scala, JRuby already being worked on

History of Concurrency
Core Java component, spymemcached, has used Futures since 2006 or so.➢ Always been ahead of most other database
like clients, however…➢ Difficult for developers to code against
○ Dumb benchmarks turn into FAQs

while (true) { client.set("foo", "bar") };

while (true) { client.set("foo", "bar") };
Signature: public OperationFuture<java.lang.Boolean> set(java.lang.String key, java.lang.Object value);

while (true) { client.set("foo", "bar").get() };

What we’re Building On
➢ A set of new components○ RxJava (both internal and public interface)○ Netty (internal only)○ LMAX Disruptor

RxJava for Couchbase
➢ Will be part of the client’s public API○ Helps us support Java 8, but is also backward
compatible■ Generally get a great Java 8 experience for free!
➢ Also using RxJava extensively internally○ Breaking the project into core and language façade
helps build toward Scala and others

Getting Referenced Elements client.asyncGet("posts::1").addListener(future -> { if (future.isDone() && !future.isCancelled()) { String content = (String) future.get(); List<String> ids = comments_ids_from_json(content);
int i = 0; for (String id : ids) { if (i++ == 5) { break; } client.asyncGet(id).addListener(future1 -> { if (future1.isDone() && !future1.isCancelled()) { System.out.println(future1.get()); } }); } } });

Getting Referenced Elements Rx Way
bucket .get("posts::1") .map(document -> document.content().getArray("comments")) .flatMap(comments -> Observable.from(comments.toList())) .take(5) .filter(o -> o instanceof String) .flatMap(o -> bucket.get((String) o)) .subscribe(doc -> System.out.println(doc.content()));

Beauty of Error Handling return super.registerBucket(name, password).flatMap(new Func1<Boolean, Observable<BucketStreamingResponse>>() { @Override public Observable<BucketStreamingResponse> call(Boolean aBoolean) { return cluster().send(new BucketStreamingRequest(TERSE_PATH, name, password)); } }).onErrorResumeNext(new Func1<Throwable, Observable<BucketStreamingResponse>>() { @Override public Observable<BucketStreamingResponse> call(Throwable throwable) { return cluster().send(new BucketStreamingRequest(VERBOSE_PATH, name, password)); } })

CouchbaseReact to our Dev Preview 2!
Michael Nitschinger’s blog:http://blog.couchbase.com/couchbase-java-sdk-200-developer-preview-2

Five Minutes of FuturesWill Sargent, Typesafe

Reactive Interface In Theory
Looks like: ● Responsive?● Scalable?● Resilient?● Event-Driven?

Reactive Interface in Practice
Really looks like this:
trait FooService { def find(fooId:FooID) : Future[Option[Foo]]}

Plain English
“Non-blocking” / “Async” means either Future[T] or Actor model

Choices
To Akka, or Not to Akka?

Akka with Futures
Akka to Future:val myFuture = actorRef ? messageFuture to Akka:futureStuff pipeTo actorRef

Assuming a Services based API
val fooFuture = fooService.get(1)val barFuture = barService.get(2)val quuxFuture = quuxService.get(3)
...Simple, right?

Not so fast
val fooFuture = fooService.get(1)val barFuture = barService.get(foo.barId)val quuxFuture = quuxService.get(bar.quuxId)
...wat.

FLATMAP THAT
fooService.get(1).map { maybeFoo => maybeFoo.flatMap { foo => val barFuture = barService.get(foo.barId) // do the same for bar & quux }}

For comprehensions!
for { maybeFoo <- fooService.get(1)} yield { for { // Option / Future does not compose foo <- maybeFoo } yield ...}

Scala Async!
async { val maybeFoo = await(fooService.get(1)) maybeFoo.flatMap { foo => val bar = await(barService.get(2)) }}

Scala Async Sadness
COMPILE ERROR.
“await must not be used inside a closure nested within an async block” == no flatMap of that.

Monads don’t compose
But before reaching for readers/transformers:● Use small methods● Short circuit● Use loan patterns

Use Small Methods
def foo2bar(ff:Future[Foo]) : Future[Bar]

Short Circuit
implicit def flatten[A](fofoa: Future[Option[Future[Option[A]]]]): Future[Option[A]] = fofoa.flatMap(_.getOrElse(Future.successful(None)))

Loan Pattern def getFuture[T](futureOptionBlock: Future[Option[T]])(foundBlock: (T => Future[Result])): Future[Result] = { futureOptionBlock.flatMap { case Some(found) => foundBlock(found) case None => Future.successful(NotFound) } }

What’s next?
Demos, food and drinks!
August 20th - NetflixOSS and Cloud Security