user

Using Closure Compiler With Webpack + Typescript via Tsickle

Using Closure Compiler With Webpack + Typescript via Tsickle

blog
Using Closure Compiler With Webpack + Typescript via Tsickle

Using Closure Compiler With Webpack + Typescript via Tsickle

 

Written by Nick Jacob

At AppMonet we serve hundreds of terabytes of javascript each day to tens of millions of devices. Our SDK is deployed in hundreds of top apps; sometimes in multiple apps on the same phones. It’s critical that we minimize our the impact of our SDK — ads should consume as few resources as possible.

Javascript bloat plagues the open web; the average webpage has 350kb of gzipped Javascript (Addy Osmani has an awesome talk about why JS file size is so problematic on mobile). Because AppMonet is mobile-only, we’re especially sensitive to the size of our Javascript files—besides lower network speeds, we’re also running in resource-constrained devices with slow javascript runtimes (Android pre-lollipop, iOS UIWebview).

Closure Compiler ADVANCED_OPTIMIZATIONS

The closure-compiler used to require running a jar (wrapped by a node module)—it’s now also available as a native binary or pure-javascript library, giving much better compile times.

In a lot of cases, closure compiler is similar in performance to uglifyJS/terser—especially if you’re able to mangle properties in uglifyjs. However, there is a special mode—ADVANCED_OPTIMIZATIONS that is really the holy grail of javascript optimization. From the closure compile docs:

The ADVANCED_OPTIMIZATIONS level compresses JavaScript well beyond what is possible with other tools.

Closure Compiler Compilation Levels | Google Developers

The Closure Compiler lets you choose from three levels of compilation, ranging from simple removal of whitespace and comments to aggressive code transformations. The WHITESPACE_ONLY compilation level removes comments from your code and also removes line breaks, unnecessary spaces, extraneous punctuation (such as parentheses and semicolons), and other whitespace.

Unfortunately using it isn’t so easy—if you read the closure compiler docs about ADVANCED_OPTIMIZATIONS, you’ll see that you not only have to carefully declare all of your “externs” (anything that can’t be mangled beyond recognition), but alsoannotate all of your code with jsdoc-like comments to achieve maximal performance.

These comments are a very es3-centric way of expressing types:


/**
 * A shape.
 * @interface
 */
function Shape() {};
Shape.prototype.draw = function() {};

/**
 * @constructor
 * @implements {Shape}
 */
function Square() {};
Square.prototype.draw = function() {
  ...
};

But if we’re using typescript, doesn’t our code already contain all of this information about types and visibility? If we could convert typescript to this annotated javascript, we’d be able to use ADVANCED_OPTIMIZATIONS without any changes to our code!

Luckily, the angular team has developed a tool called tsickle, which does exactly that! From the tsickle github:


* inserts closure-compatible JSDoc annotations on functions/classes/etc
* converts ES6 modules into goog.module modules
* generates externs.js from TypeScript d.ts (and declare, see below)
* declares types for class member variables
* translates export * from ... into a form Closure accepts
* converts TypeScript enums into a form Closure accepts
* reprocesses all jsdoc to strip Closure-invalid tags

Awesome! Unfortunately, tsickle isn’t exactly mainstream:

We already use tsickle within Google to minify our apps (including those using Angular), but we have less experience using tsickle with the various JavaScript builds that are seen outside of Google.

How can we use tsickle to transform typescript in our existing webpack (and/or) rollup based projects? The tsickle readme mentions that it’s a drop-in replacement for tsc, but does that mean we can use it directly with ts-loader? Luckily Inseok Lee has provided an example webpack loader as a github gist.

This loader is using the typescript compiler to load and process the tsconfig.json file, and then runs tsickle.emitWithTsickle to transform typescript. In our webpack config, we’ll replace ts-loader with this localtsickle-loader. This will produce an annotated javascript source:

You won’t actually see this annotated JS—the loader passes it through to the the rest of the webpack build process (e.g., the next loader in the chain).

This means that we can pass the tsickle-annotated javascript closure compiler in the webpack optimization stage:

Note that we use ModuleConcatenationPlugin; this is just to explicitly enable tree-shaking: closure compiler will perform advanced dead-code elimination, and if you output ES5 modules from it, webpack can reduce its bundling overhead via tree-shaking.

To use this setup in production, we wanted to add some tests to this loader and cover some edge cases in tsickle compilation:

  • handling multiple typescript module types (es5 output is optimal for tree-shaking)
  • allow passing options to specify the tsconfig.json and the location of the externs.js file
  • fix some issues in tsickle output (caused by using it in a webpack loader)

We’ve open sourced our implementation of tsickle-loader:

AppMonet/tsickle-loader

This is a webpack loader for tsickle; it lets us compile typescript code with the typescript compilaer, while adding annotations & externs readable by closure compiler, which means we can use ADVANCED_OPTIMIZATIONS mode. See the directory for an example of compiling this way.

Used with google-closure-compiler and the relatively newclosure-webpack-plugin, we can build some very optimized javascript. Given this arbitrary example:

closure-compiler outputs 1,333 bytes — the webpack terser default produces 1,466 bytes. These numbers aren’t that useful, since most of that output is the webpack bundler API. However you can see the closure-compiler output is able to mangle the class properties in a way that will help reduce bundle size as the code’s complexity grows.

Here’s the closure compiler output beautified so it’s easier to read:

Just 10kb of javascript reduced across 100 million SDK sessions will reduce bandwidth by 1TB. More importantly, it will help reduce resource usage across millions of phones and improve user experience.

You can see that tsickle+closure compiler correctly preserved the structure of the PublicAPI interface.

When you combine this with typescript’s module: "es2015" and use webpack’s tree-shaking (or, port the tsickle-loader to rollup), you can really reduce the impact of using utility libraries or 3rd-party code in your library—widely repeated imports are well-mangled, dead code is eliminated, and the remaining code is heavily optimized.

The closure compiler will also include any necessary polyfills based on your actual code usage and the target output language: es5 array methods are included individually, so you aren’t shipping a shim for findIndex if you only ever use find.

In some tests, we see smaller bundle sizes as the application gets larger:

Using Closure Compiler With Webpack + Typescript via Tsickle

Just 10kb of javascript reduced across 100 million SDK sessions will reduce bandwidth by 1TB. More importantly, it will help reduce resource usage across millions of phones and improve user experience.

Drive revenue and boost engagement. Find out how.

Read more
How to maximize Kinesis write throughput with Elixir

How to maximize Kinesis write throughput with Elixir

blog

How to maximize Kinesis write throughput with Elixir

 

Written by Nico Piderman

How to maximize Kinesis write throughput with Elixir

At AppMonet we help mobile app developers maximize their advertising revenue by running auctions each time an ad slot is available on their users’ devices. This activity generates tons of data, almost all of which enters our system through AWS Kinesis.

We write billions of events to Kinesis each day, and if one isn’t careful, this kind of activity can end up costing a lot of money. The most efficient way to use the service is demonstrated by Amazon’s official Kinesis Producer Library (KPL). However, this library can only be used with Java. The features of this library that we are most interested in include:

Amazon also offers a series of aggregation libraries in several other popular languages which would allow you to aggregate your records using the same technique as the KPL, but leaves the actual transmission of the aggregated records to Kinesis up to the user. Sadly there is no official Elixir/Erlang aggregation library either.

However, the fine people over at AdRoll also make heavy use of Kinesis streams, and have published several open source libraries to help the BEAM community efficiently interact with Kinesis.

Erlmld and Exmld are two libraries made to help efficiently and safely consume Kinesis streams. Erlmld in particular, comes with a port of the Python implementation of the official Kinesis record aggregator. We extracted the producing logic and ported it to Elixir.

We have begun to use it internally because we didn’t want to pull in the entire Exmld package, which is for consuming streams, just to use this one aggregation module.

ProvisionedThroughputExceededException

Once you start writing a non trivial amount of data to your first Kinesis stream, you will inevitably come across the “ProvisionedThroughputExceededException”. Don’t let this exception scare you in to scaling up your streams (and increasing your AWS bill) just yet!

Instead, your application should retry requests that produce this exception. The ExAws Elixir library comes with a great implementation of exponential backoff with jitter, and for many AWS services, this retry strategy will kick in automatically.

Unfortunately, we found that Kinesis Put Record API calls made with ExAwsKinesis do not trigger these automatic retries. We have patched the library and submitted our fix upstream and are waiting to hear back from the package maintainers.

For now, we are using our patched version with great success and enjoying excellent throughput on a minimal number of shards!

Practical Architecture

So what does this all look like in practice? There are three main pieces in our application.

  1. Poolboy pool
  2. GenServers
  3. Module to manage the GenServer state

We use a pool, mainly so no single GenServer becomes a bottle neck in our application.

You can adjust the size and max overflow of the pool according to your needs. We use the fifo strategy, so that we round robin through the pool and use all of the GenServers.

The GenServers look something like:

This GenServer provides an API function, write/1 which will check out a process from our pool, and push a new record to the GenServer’s state, which uses our KPL aggregator to pack it up and hold it until we are ready to send a batch to Kinesis.

The other important part to note here is the call to `Process.flag(:trap_exit, true)`, along with the implementation of the terminate/2 callback. These should ensure that any records that are being buffered in the GenServer state, are not lost when one of these GenServers is terminated.

Lastly, let’s take a look at the module that will manage the GenServer state:

This module is just a wrapper around the ExKpl aggregator. This module only exposes 3 functions.new/1 will return a new aggregator. write/2 adds a new record to the aggregator, and will automatically flush once the aggregated record has reached the maximum size, and return a new aggregator. Finally flush/2 allows us to flush the aggregator and send all accumulated records to Kinesis. In some applications we also track the time of the last flush, so that we can have the aggregators flush if the elapsed time is greater than a certain threshold, but if your volume is high enough, you probably don’t need to worry about that.

This example uses the put_record API method, but you should also explore put_records as this method can send up to 500 records per request, each of which can be up to 1mb, so 5 of our carefully aggregated records a time.

Drive revenue and boost engagement. Find out how.

Read more