Webpack Hot Reloading and React

December 29th, 2015 | hot reloading, javascript, react, webpack |

This post is an extract of a github repo I’m working on chtefi/react-stack-step-by-step to explain step-by-step from scratch a full reactjs stack

– You already use HR ?

If you have a project using webpack, React, you probably have followed some tutorials and blogs to add the Hot Reloading piece.

Here, we are going to explain what’s going on under the hood, and how the HR projects / plugins you installed are working together (webpack HR middlewares, react-transform..).

We are going to use the latest React HR project in date react-transform-hmr and not the older one react-hot-loader which has been sentenced to the maximum penalty : the-death-of-react-hot-loader. I guess it’s still working properly but you know that the Javascript community is always on the bleeding edge side!

– You don’t already use HR ?

If the HR part does not sound familiar to you, that’s fine because I’m going to explain step-by-step why it’s mandatory to use HR nowadays, and what is the way to install and use it into your project.

 

Let’s just start by resuming why everybody is now talking about HR (beyond Javascript).

Why should we enjoy Hot Reloading ?

In one word : productivity.
In another word : DX (Developer eXperience).

Some more explanations :

– you broke your F5 key years ago, you can’t refresh anymore
– you don’t want to develop blindly, then refresh, then try again. It’s a backend thing. We have a UI, it’s alive
– you don’t want to lose your application state (by doing a full refresh) if you just fixed a typo or a color
– that can display your compilation and runtime errors directly in the browser in the exact spot in the UI where it’s used
– you can do HR with Javascript, CSS, anything. We’ll just focus on React components here.
– no caveat

As soon as you put it in place, it will work forever without any modifications, for any of the future files you’re going to add. It’s just some pipes to plug together to make it work and last forever.

2015-12-28: actually there is only one caveat (!) : the React HR plugin we are going to use does not handle the stateless functional components created with the simpler React v0.14 syntax : const App = (props) => {

Hello {props.name}
};.
Refer to comment.

Now, let’s tackle the packages we need to install to use it in a nodejs project.

What packages to install to use HR ?

Let’s suppose you already have a base project using :

webpack to compile your javascript bundle(s)
React for your frontend ofc
– a nodejs server that runs a http server such as expressjs to serve your static content (html, js, css..) while you’re developing

You now want to experiment HR.

webpack to the rescue

webpack is actually the main actor dealing with the HR, it already exposes an API to process some part of the HR pipeline.

We just need to add some wrapper around to use its API, and some more logic to manage the specific React components state : you don’t want to lose the current state of your components when you change something in a js file (a style, some constant, a prop, add a React component inside an existing one etc.).

We need to install 4 packages : 2 for webpack, 2 for React.

webpack-dev-middleware
– a classic expressjs middleware, where requests are passed on
– it automatically watches the sources for changes to recompile the javascript bundle server side if some javascript source have changed
– it always serves the bundle up to date

webpack-hot-middleware
– a classic expressjs middleware, where requests are passed on
– it automatically subscribes to the bundle recompilation events (such as “start”, “done”), to notify the frontend that something has changed and it need to update itself
– it uses SSE to communicate with the frontend

Specific packages for React HR

babel-plugin-react-transform
– it can add any code around React component methods during the Babel compilation ES6+JSX to ES5. You should already have configured your babel loader in webpack, such as :

 module: {
    loaders: [{
      test: /\.js$/,
      loader: 'babel',
      include: path.join(__dirname, 'src'),
    }]
  },

react-transform-hmr
– it is used by babel-plugin-react-transform to add specific code around the React components to properly handle HR and their current state

That gives us :

$ npm install --save-dev webpack-dev-middleware
$ npm install --save-dev webpack-hot-middleware
$ npm install --save-dev babel-plugin-react-transform@beta
$ npm install --save-dev react-transform-hmr
// or for the copy/paste, all together :
$ npm i -D webpack-dev-middleware webpack-hot-middleware babel-plugin-react-transform@beta react-transform-hmr

2015-12-28: we explicitely ask for the beta (>=2.0.0) of babel-plugin-react-transform because for now, the latest published version does not work with Babel 6. But work has been done and is just waiting to be merged.

 

Now that we have installed the necessary packages, it’s time to configure them.

Configure Babel

We need to configure Babel (we are going to use .babelrc) to use babel-plugin-react-transform and react-transform-hmr to add the HR code around them.

The most basic configuration is :

{
  "presets": ["react", "es2015"],
  "env": {
    "development": {
      "plugins": [
        ["react-transform", {
          "transforms": [{
            "transform": "react-transform-hmr",
            "imports": ["react"],
            "locals": ["module"]
          }]
        }]
      ]
    }
  }
}

Basically :
– that adds the transform babel-plugin-react-transform for the development NODE_ENV only
– this transform retrieve all the React components it can find in the source code
– it passes them down to each of its processors defined in "transforms" to let them add their custom code. (in our case, react-transform-hmr will add the HR code)

 

For the record, babel-plugin-react-transform handles as many "transforms" as we want. They are just going to be called right after each other. For instance :

{
  "presets": ["react", "es2015"],
  "env": {
    "development": {
      "plugins": [
        ["react-transform", {
          "transforms": [{
            "transform": "react-transform-hmr",
            "imports": ["react"],
            "locals": ["module"]
          }, {
            "transform": "react-transform-catch-errors",
            "imports": ["react", "redbox-react"]
          }]
        }]
      ]
    }
  }
}

The React component code will pass through react-transform-hmr, then through react-transform-catch-errors.
Each of them will add its code around each components.

FYI, the latter is used to catch errors that are thrown in the render() method of the React components. Then it’s using its "imports" property to redirect the error to a visual React component. Here redbox-react displays a big red screen of the death with the stacktrace for instance. But it could be anything else.

Basically, react-transform-catch-errors just adds try { render() } catch (e) { ... } around the original render() method of your components, and in the catch, it’s returning the React component you gave in "imports". Makes sense right ?

Now, our React code is ready to handle HR.
We now have to make the server communicate to the browser that the code has changed and that it needs to update.

Handle server/client communication to send/receive HR updates

Bundle recompilation on the fly

First, we need to make the server aware that the source code has changed to recompile the bundle, and then notify the browser.
That’s the role of webpack-dev-middleware and webpack-hot-middleware.

webpack-dev-middleware will automatically start to watch the source code for changes and recompile the bundle
webpack-hot-middleware will be notified a new bundle is compiled and will notify the browser

We just need to plug them into expressjs as middlewares to start them :

var express = require('express');
var webpack = require('webpack');
var path = require('path');

var webpackDevMiddleware = require("webpack-dev-middleware");
var webpackHotMiddleware = require("webpack-hot-middleware");
var webpackConfig = require('../webpack.config');

var app = express();

var compiler = webpack(webpackConfig);
app.use(webpackDevMiddleware(compiler));
app.use(webpackHotMiddleware(compiler));
app.use(express.static('src'));

app.listen(3000);

But how is the browser going to handle the updates ? It’s where webpack itself rises.

Browser live update

We need to add some code client-side to deal with it, otherwise, it’s not possible. HR is not browser native or anything.

Therefore, to inject some more code, we will use the webpack bundle entry point in webpack.config.js, to add another entry.
A bundle could have several entry points.
It is just there to say to webpack : “hey, resolve the import dependency (for make the bundle) tree starting from those files!”.

entry: [
  'webpack-hot-middleware/client',
  path.join(__dirname, 'src', 'App.js'),
],

The entrypoint webpack-hot-middleware/client simply refers to the file node_modules/webpack-hot-middleware/client.js.
It is the file that contains the code that will be used in the browser to handle the SSE communication with the server (to intercept the update notifications).

Then we need to add a specific webpack internal plugin HotModuleReplacementPlugin to expose the generic webpack HR API in the browser :

plugins: [
  new webpack.optimize.OccurenceOrderPlugin(), // recommanded by webpack
  new webpack.HotModuleReplacementPlugin(),
  new webpack.NoErrorsPlugin() // recommanded by webpack
]

This API will be used by the code injected from webpack-hot-middleware/client when this one will retrieve some “update” events through its SSE. (specifically, it will call module.hot.apply(..) from HotModuleReplacementPlugin).
You follow ?

 

Nothing more to do, you’re good to go !

See it in action

Process :
– start your nodejs server
– go to your page using the bundle on your browser
– go to your code editor and change some javascript bits
– see the live update !
– if not, check your console in the browser to see some nice error messages

Behind the scene :
– the server automatically recompile the bundle if some javascript code is updated and notify the browser to update itself via SSE
– the bundle used in the browser contains some SSE code to intercept those notifications, some generic HR code to “patch” the javascript, and some custom HR code for the React components to not lose their current state

A boilerplate on github

You can checkout this boilerplate from @dan_abramov https://github.com/gaearon/react-transform-boilerplate to give a try.
It’s very simple and does exactly what we just talked about.
You will see some more options used but nothing fancy.

Its .babelrc is using react-hmre instead of our two transforms packages (babel-plugin-react-transform@beta and react-transform-hmr), but it is exactly the same actually. react-hmre simply encapsulates them.

More bits to learn

Let’s explain some more in-depth some aspects of the code used.

webpack-dev-middleware is optional, but…

Without webpack-dev-middleware, you just need to launch the webpack watch yourself :

// app.use(webpackDevMiddleware(compiler));
// replace the dev-middleware with a simple watch() (args are mandatory)
compiler.watch({}, function(){});
app.use(webpackHotMiddleware(compiler));

Because webpackHotMiddleware subscribes to the bundle compilation events (no matter what started it), it will work.

But you’ll suffer from some consequences : a bunch of .js and .json files in your project will appears each time a compilation occurs.

They contain the delta sent to the client to update itself (webpack only sends the updated chunks, not the whole bundle each time). The advantage of using webpack-dev-middleware is that you won’t see those files. They will be handled in-memory by itself. That’s why you need to install this particular package too.

babel-plugin-react-transform and react-transform-hmr

Without the code added by react-transform-hmr, webpack would not be able to hot update the React components, you would get this in the browser console :

[HMR] bundle rebuilding
[HMR] bundle rebuilt in 160ms
[HMR] Checking for updates on the server...
[HMR] The following modules couldn't be hot updated: (Full reload needed)
[HMR]  - ./src/App.js

What’s inside the SSE ?

1. The browser initializes a SSE request : (thanks to the code webpack-hot-middleware/client.js injected in the bundle) on this specific url : GET localhost:3000/__webpack_hmr (it never returns). It’s handled by the server that knows it’s SSE. (by the webpack-hot-middleware expressjs middleware)

Then if a Javascript file is edited, because webpack-dev-middleware started a watch on the sources, webpack-hot-middleware is notified (because it subscribed to the recompilation events) and notify the frontend via SSE with a new module map (used by webpack), such as :

data: {"action":"building"}

// few ms later ...

data: {"action":"built","time":260,"hash":"6b625811aa23ea1ec259","warnings":[],"errors":[],"modules":{"0":"multi main","1":"./~/fbjs/lib/invariant.js","2":"./~/react/lib/Object.assign.js","3":"./~/fbjs/lib/warning.js","4":"./~/fbjs/lib/ExecutionEnvironment.js","5":"./~/react/lib/ReactMount.js","6":"./~/react/lib/ReactElement.js", ...

Then the frontend asks for those 2 files:
(I guess the hash used in the GET comes from the SSE data ? I didn’t check)

GET localhost:3000/0.0119cbdcd4c2cf8d27c2.hot-update.js

{"h":"6b625811aa23ea1ec259","c":[0]}

GET localhost:3000/0119cbdcd4c2cf8d27c2.hot-update.json

webpackHotUpdate(0,{

/***/ 97:
/***/ function(module, exports, __webpack_require__) {

  /* WEBPACK VAR INJECTION */(function(module) {'use strict';

  ...

Those particular urls are served by webpack-dev-middleware which is keeping those files (*.hot-update.js[on]) in memory and serves them as any classic static file. They are requested by the code injected by webpack.HotModuleReplacementPlugin() that handles the responses and hot-updates the javascript with the new code.

Conclusion

Before I dug into, I was finding that the HR was a bit complicated to understand how it works, who are the actors, how does it communicate, etc. I was never sure what package was used for what exactly, and when. I hope those explanations were clear enough and that now, you know as much as me ! (or more!) Don’t hesitate to add more details or correct me if I’m wrong.

Finally, it’s not that complex but it just need a lot of “pipes” to plug everything together. But it all makes sense.

Unfortunately, there are still some caveats with the way I exposed :
– it doesn’t work for functional stateless React components const App = (props) => {

Hello {props.name}
};
babel-plugin-react-transform is simply an experiment, maybe it will die
– is working on to handle HR at the function level directly, to make it more generic
webpack is an awesome tool

HBase merge and split impact in HDFS

December 24th, 2015 | cloudera, hadoop, hbase, hdfs |

Why merge ?

I had a table with a lot of regions (almost a thousand), more than I wanted, and more than I should, according to the HBase book.
The max size of the HFile hbase.hregion.max.filesize was 10G.
I raised it to 20G and recompact the whole table, thinking I’m done. But nothing happened.
Why ? Because HBase does not merge regions automatically.

Compaction is used to merge Storefiles in a same HStore (one HStore per column family, per region).
A region that exists, is going to exist forever. Except if we delete it or merge it manually.

I then decided to merge some regions, first to give a try, and second to see the impact in HDFS, because I’m curious.
If you are wondering too, you’re in the good place, keep reading. It’s not complex.

I’ll first do a merge without raising hbase.hregion.max.filesize, to see what is happening.
Then I’ll raise the max and do another merge, and check the differences.

HBase version : 1.0.0-cdh5.4.7
Hadoop Version : 2.6.0-cdh5.4.7

Merge still at 10G max

First, you need to find 2 consecutive regions to merge together.
The consecutivity is important, you can merge region that are not consecutive but it’s not recommanded (overlapping).
e.g. : If you have 2 regions which start/end keys are 0-9 and 9-A, you want to create a region which start/end keys are 0-A.

In HDFS, there is no order, it’s all guids. To know to what they correspond, one way is to go to the HBase admin and select the table.
That will display every of its regions name, uuid, start/end keys.

http://hadoopmaster:60010/table.jsp?name=my_table
# or :16010 if recent

A region name is something like :

my_table,0115d0b6f99f58a34...2a9e72781c7,1440840915183.fbb00c100422d1cc0f9b7e39d6c6bd91.
# signification :
[table],[start key],[timestamp].[encoded ID]

The encoded ID is what we are interested into. This is the folder in HDFS (/hbase/data/default/my_table/fbb00c100422d1cc0f9b7e39d6c6bd91) where the data of this region are.

Let’s merge it with its following one.

hbase> merge_region 'fbb00c100422d1cc0f9b7e39d6c6bd91', ‘a12acd303c0b7e512c8926666c5f02eb'

That creates a new region 65bd... that contains a HFile which size is growing slowly, as we can see in HDFS :
(here is a diff from before and after the merge_region)

>          0 2015-12-23 12:24 .../my_table/65bd82b5477fcc2090804c351d89700a
>        226 2015-12-23 12:24 .../my_table/65bd82b5477fcc2090804c351d89700a/.regioninfo
>          0 2015-12-23 12:24 .../my_table/65bd82b5477fcc2090804c351d89700a/.tmp
> 2684354560 2015-12-23 12:24 .../my_table/65bd82b5477fcc2090804c351d89700a/.tmp/752530e58ae8478d812696b066edcc9f
>          0 2015-12-23 12:24 .../my_table/65bd82b5477fcc2090804c351d89700a/recovered.edits
>          0 2015-12-23 12:24 .../my_table/65bd82b5477fcc2090804c351d89700a/recovered.edits/2206186528.seqid
>          0 2015-12-23 12:24 .../my_table/65bd82b5477fcc2090804c351d89700a/t
>        109 2015-12-23 12:24 .../my_table/65bd82b5477fcc2090804c351d89700a/t/ccd883e710664f1fbf605590deaf2868.a12acd303c0b7e512c8926666c5f02eb
>        109 2015-12-23 12:24 .../my_table/65bd82b5477fcc2090804c351d89700a/t/e17b4ea9b9fa47c1839999426ef9ffe7.fbb00c100422d1cc0f9b7e39d6c6bd91

<          0 2015-12-23 12:13 .../my_table/a12acd303c0b7e512c8926666c5f02eb/recovered.edits
<          0 2015-12-23 12:13 .../my_table/a12acd303c0b7e512c8926666c5f02eb/recovered.edits/2198106631.seqid
---
>          0 2015-12-23 12:24 .../my_table/a12acd303c0b7e512c8926666c5f02eb/recovered.edits
>          0 2015-12-23 12:24 .../my_table/a12acd303c0b7e512c8926666c5f02eb/recovered.edits/2198106637.seqid

<          0 2015-12-23 11:45 .../my_table/fbb00c100422d1cc0f9b7e39d6c6bd91
---
>          0 2015-12-23 12:24 .../my_table/fbb00c100422d1cc0f9b7e39d6c6bd91
>          0 2015-12-23 12:24 .../my_table/fbb00c100422d1cc0f9b7e39d6c6bd91/.merges

<          0 2015-12-23 12:13 .../my_table/fbb00c100422d1cc0f9b7e39d6c6bd91/recovered.edits
<          0 2015-12-23 12:13 .../my_table/fbb00c100422d1cc0f9b7e39d6c6bd91/recovered.edits/2206186546.seqid
---
>          0 2015-12-23 12:24 .../my_table/fbb00c100422d1cc0f9b7e39d6c6bd91/recovered.edits
>          0 2015-12-23 12:24 .../my_table/fbb00c100422d1cc0f9b7e39d6c6bd91/recovered.edits/2206186549.seqid

What we can see :
new region in the folder 65b... with a HFile in .tmp (2.7GB, growing) and a .regioninfo (very important, that’s the metadata to identify what is this region)
– new empty folder .merges in one of the region we are merging
recovered.edits folders. Don’t mind them, I won’t display them anymore. For more info, check this nice Cloudera blog post to know more about it.

After a few minutes, it was done, the HFile grew up to 17GB (which was over the limit of 10GB).
HBase started the reverse process : it split the big region I just made ! :-(

>           0 2015-12-23 13:05 .../my_table/2c142664dc0929d7c6cc5fa6fe3b4e40
>         226 2015-12-23 13:05 .../my_table/2c142664dc0929d7c6cc5fa6fe3b4e40/.regioninfo
>           0 2015-12-23 13:05 .../my_table/2c142664dc0929d7c6cc5fa6fe3b4e40/t
>         109 2015-12-23 13:05 .../my_table/2c142664dc0929d7c6cc5fa6fe3b4e40/t/752530e58ae8478d812696b066edcc9f.65bd82b5477fcc2090804c351d89700a

>           0 2015-12-23 13:05 .../my_table/65bd82b5477fcc2090804c351d89700a
>         226 2015-12-23 12:24 .../my_table/65bd82b5477fcc2090804c351d89700a/.regioninfo
>           0 2015-12-23 13:05 .../my_table/65bd82b5477fcc2090804c351d89700a/.splits
>           0 2015-12-23 13:05 .../my_table/65bd82b5477fcc2090804c351d89700a/.tmp
>           0 2015-12-23 13:05 .../my_table/65bd82b5477fcc2090804c351d89700a/t
> 17860937303 2015-12-23 13:05 .../my_table/65bd82b5477fcc2090804c351d89700a/t/752530e58ae8478d812696b066edcc9f

>           0 2015-12-23 13:05 .../my_table/743bfa035be56bf412d00803abe433b8
>         226 2015-12-23 13:05 .../my_table/743bfa035be56bf412d00803abe433b8/.regioninfo
>           0 2015-12-23 13:05 .../my_table/743bfa035be56bf412d00803abe433b8/.tmp
>   134217728 2015-12-23 13:05 .../my_table/743bfa035be56bf412d00803abe433b8/.tmp/e377603958894f8ca1ec598112b95bf4
>           0 2015-12-23 13:05 .../my_table/743bfa035be56bf412d00803abe433b8/t
>         109 2015-12-23 13:05 .../my_table/743bfa035be56bf412d00803abe433b8/t/752530e58ae8478d812696b066edcc9f.65bd82b5477fcc2090804c351d89700a

<           0 2015-12-23 11:45 .../my_table/a12acd303c0b7e512c8926666c5f02eb

<           0 2015-12-23 11:45 .../my_table/fbb00c100422d1cc0f9b7e39d6c6bd91

– the two old regions are removed (a12... and fbb...)
– the split region has a .splits folder
– 2 new regions appeared : 2c1... and 743...
– only one of these 2 regions have a HFile that is slowly growing (meaning: sequential process)

Meanwhile, in the logs…

// 2 new regions from a SPLIT
2015-12-23 13:05:32,817 INFO org.apache.hadoop.hbase.master.RegionStates: Transition null to {2c142664dc0929d7c6cc5fa6fe3b4e40 state=SPLITTING_NEW, ts=1450872332817, server=hadoopslave04,60020,1450869198826}
2015-12-23 13:05:32,817 INFO org.apache.hadoop.hbase.master.RegionStates: Transition null to {743bfa035be56bf412d00803abe433b8 state=SPLITTING_NEW, ts=1450872332817, server=hadoopslave04,60020,1450869198826}

// the region we are splitting was OPEN
// it goes to SPLITTING then SPLIT, and is set offline for the time being
2015-12-23 13:05:32,817 INFO org.apache.hadoop.hbase.master.RegionStates: Transition {65bd82b5477fcc2090804c351d89700a state=OPEN, ts=1450869854560, server=hadoopslave04,60020,1450869198826} to {65bd82b5477fcc2090804c351d89700a state=SPLITTING, ts=1450872332817, server=hadoopslave04,60020,1450869198826}
2015-12-23 13:05:34,767 INFO org.apache.hadoop.hbase.master.RegionStates: Transition {65bd82b5477fcc2090804c351d89700a state=SPLITTING, ts=1450872334767, server=hadoopslave04,60020,1450869198826} to {65bd82b5477fcc2090804c351d89700a state=SPLIT, ts=1450872334767, server=hadoopslave04,60020,1450869198826}
2015-12-23 13:05:34,767 INFO org.apache.hadoop.hbase.master.RegionStates: Offlined 65bd82b5477fcc2090804c351d89700a from hadoopslave04,60020,1450869198826

// both 2 new regions switch from SPLITTING_NEW to OPEN
2015-12-23 13:05:34,767 INFO org.apache.hadoop.hbase.master.RegionStates: Transition {2c142664dc0929d7c6cc5fa6fe3b4e40 state=SPLITTING_NEW, ts=1450872334767, server=hadoopslave04,60020,1450869198826} to {2c142664dc0929d7c6cc5fa6fe3b4e40 state=OPEN, ts=1450872334767, server=hadoopslave04,60020,1450869198826}
2015-12-23 13:05:34,767 INFO org.apache.hadoop.hbase.master.RegionStates: Transition {743bfa035be56bf412d00803abe433b8 state=SPLITTING_NEW, ts=1450872334767, server=hadoopslave04,60020,1450869198826} to {743bfa035be56bf412d00803abe433b8 state=OPEN, ts=1450872334767, server=hadoopslave04,60020,1450869198826}

// daughter a and b = new regions with start keys; the parent being the split region
2015-12-23 13:05:34,873 INFO org.apache.hadoop.hbase.master.AssignmentManager: Handled SPLIT event; parent=my_table,fe7f...,1450869853820.65bd82b5477fcc2090804c351d89700a., daughter a=my_table,fe7f...,1450872332556.2c142664dc0929d7c6cc5fa6fe3b4e40., daughter b=my_table,feff7...,1450872332556.743bfa035be56bf412d00803abe433b8., on hadoopslave04,60020,1450869198826

// then the split region saw it reference in the metadata (zookeeper) removed
2015-12-23 13:08:28,965 INFO org.apache.hadoop.hbase.MetaTableAccessor: Deleted references in merged region my_table,fe7f...,1450869853820.65bd82b5477fcc2090804c351d89700a., qualifier=mergeA and qualifier=mergeB

Back to HDFS

After a while, HBase is done with the daughter b region 743b..., and starts creating the daughter a region 2c14....

>           0 2015-12-23 13:25 .../my_table/2c142664dc0929d7c6cc5fa6fe3b4e40
>         226 2015-12-23 13:05 .../my_table/2c142664dc0929d7c6cc5fa6fe3b4e40/.regioninfo
>           0 2015-12-23 13:41 .../my_table/2c142664dc0929d7c6cc5fa6fe3b4e40/.tmp
>           0 2015-12-23 13:41 .../my_table/2c142664dc0929d7c6cc5fa6fe3b4e40/t
>  8732040437 2015-12-23 13:41 .../my_table/2c142664dc0929d7c6cc5fa6fe3b4e40/t/2388513b0d55429888478924914af494

>           0 2015-12-23 13:05 .../my_table/65bd82b5477fcc2090804c351d89700a
>         226 2015-12-23 12:24 .../my_table/65bd82b5477fcc2090804c351d89700a/.regioninfo
>           0 2015-12-23 13:05 .../my_table/65bd82b5477fcc2090804c351d89700a/.splits
>           0 2015-12-23 13:05 .../my_table/65bd82b5477fcc2090804c351d89700a/.tmp
>           0 2015-12-23 13:05 .../my_table/65bd82b5477fcc2090804c351d89700a/t
> 17860937303 2015-12-23 13:05 .../my_table/65bd82b5477fcc2090804c351d89700a/t/752530e58ae8478d812696b066edcc9f

>           0 2015-12-23 13:05 .../my_table/743bfa035be56bf412d00803abe433b8
>         226 2015-12-23 13:05 .../my_table/743bfa035be56bf412d00803abe433b8/.regioninfo
>           0 2015-12-23 13:25 .../my_table/743bfa035be56bf412d00803abe433b8/.tmp
>           0 2015-12-23 13:25 .../my_table/743bfa035be56bf412d00803abe433b8/t
>  8733203481 2015-12-23 13:25 .../my_table/743bfa035be56bf412d00803abe433b8/t/e377603958894f8ca1ec598112b95bf4

It’s done. The region has been successfully split.
After a few minutes, the big region 65bd... will be removed automatically.

2015-12-23 13:43:28,908 INFO org.apache.hadoop.hbase.MetaTableAccessor: Deleted my_table,fe7f...,1450869853820.65bd82b5477fcc2090804c351d89700a.
2015-12-23 13:43:28,908 INFO org.apache.hadoop.hbase.master.CatalogJanitor: Scanned 722 catalog row(s), gc'd 0 unreferenced merged region(s) and 1 unreferenced parent region(s)

Note: if we do the difference between the sum of the daughter region sizes and the big one, we get a delta of +395MB (the single HFile is bigger).

We’ve successfully merged 2 regions into one, that was automatically split into two. Hurray !

Raising hbase.hregion.max.filesize to avoid splitting

Now, let’s change hbase.hregion.max.filesize in Cloudera to 20G and merge again to get a big one, without split.

We apply the same process than before and merge manually the 2 regions we got previously 2c14... and 743b....
That creates a new region 1e64... which size is surprisingly lesser than our previous merge (we only get a delta of 212KB) that is not going to be split.

$ hdfs dfs -ls -R /hbase/data/default/my_table/1e64aa6f3f5cf067f6d5339230ef6db7
-rw-r--r--   3 hbase hbase         226 2015-12-23 13:45 /hbase/data/default/my_table/1e64aa6f3f5cf067f6d5339230ef6db7/.regioninfo
drwxr-xr-x   - hbase hbase           0 2015-12-23 14:12 /hbase/data/default/my_table/1e64aa6f3f5cf067f6d5339230ef6db7/.tmp
drwxr-xr-x   - hbase hbase           0 2015-12-23 13:48 /hbase/data/default/my_table/1e64aa6f3f5cf067f6d5339230ef6db7/recovered.edits
-rw-r--r--   3 hbase hbase           0 2015-12-23 13:48 /hbase/data/default/my_table/1e64aa6f3f5cf067f6d5339230ef6db7/recovered.edits/2206186536.seqid
drwxr-xr-x   - hbase hbase           0 2015-12-23 14:12 /hbase/data/default/my_table/1e64aa6f3f5cf067f6d5339230ef6db7/t
-rw-r--r--   3 hbase hbase 17465031518 2015-12-23 14:12 /hbase/data/default/my_table/1e64aa6f3f5cf067f6d5339230ef6db7/t/d1109c52de404b0c9d07e2e9c7fdeb5e

So that worked, I have one less region ! Let’s continue why the thousands more I have.

Why should I know this ?

Knowing what’s going on in HDFS with HBase is important when you are facing issues and errors in HBase table structure.
To know if you have that kind of issues, just give a try with :

$ hbase hbck my_table

If you see some ERRORS such as :
– No HDFS region dir found
– Region not deployed on any region server.
– Region found in META, but not in HDFS or deployed on any region server.
– First region should start with an empty key. You need to create a new region and regioninfo in HDFS to plug the hole.
– You need to create a new .regioninfo and region dir in hdfs to plug the hole.
– ERROR: Found lingering reference file hdfs://…

That’s going to be a bit useful. (and maybe complicated for you, that depends of the problem!)

That can happen quite easily unfortunately (I successfully ran into those issue just with a merge_region, not sure why exactly).

React inline styles VS CSS : benchmark

August 17th, 2015 | javascript, performance, react |

CSS vs Inline

So, I was wondering if there was any performance downside to use React inline styles (I like them!) vs keep the old plain css stylesheets (that we love to override!). Of course there is, but I wanted some numbers!

I think both ways are useful actually, as Michael Chan said at React Europe ():
– the css can be used for the layout (such as the bootstrap layout classes: row-fluid, span*, etc.)
– the inline styles should be used for the “state-styles” (the style that can change according to a state, which is dynamic).

Why stupid

I decided to try something stupid and not truly relevant but anyway :

Generate a big table, one with inline styles, the other (in another page), with the same data, but with css, and then check the performance/size/time of both.

That’s why it’s not relevant because you would not generate a big table in your DOM. You will more likely have some kind of virtual scrolling / lazy rendering.
But, what about a test, just to see if that’s more slow when I scroll or not, and what about the rendering time ?

 
Chrome 45b, React 0.13.3 (debug and production)

Test case

So I generated a

of 1,000 rows with 9 columns, all of them with the same content “”, then I did the same with 10,000 rows to check the trend.

I’m going to test :
– the smoothness when I scroll down the page through Chrome DevTools Timeline
size of the DOM : useful to know for isomorphic universal javascript (server-side rendering)
– time to mount the DOM : I’m going to use componentDidMount, this is where the DOM is available after render is called
– time to be rendered by the browser : I’m going to use requestAnimationFrame to know when the next frame after render is ready

Basically, the skeleton of the React component is :

const createOneInlineStyleRow = (i) => {
  return (
... ); }; export default class AppInline extends React.Component { componentDidMount() { console.timeEnd('didMount'); } render() { console.time('render'); console.time('didMount'); requestAnimationFrame(function() { console.timeEnd('render'); }); const rows = times(NB_ROWS, createOneInlineStyleRow); return (
{rows}
); } }

The style of the :

const style = {
  fontFamily: 'Consolas',
  padding: 10,
  color: "#444",
  border: "3px solid orange",
  position: "relative",
  width: "15%",
  height: "25px",
  letterSpacing: 0,
  overflow: "hidden",
  fontSize: 10,
  fontVariant: "small-caps"
};

And the same in css for the css table :

.tableCss td {
   font-family: Consolas;
   padding: 10px;
   color: #444;
   border: none;
   border: 3px solid orange;
   position: relative;
   width: 15%;
   height: 25px;
   letter-spacing: 0;
   overflow: hidden;
   font-size: 10px;
   font-variant: small-caps;
}

Basically, here is this beautiful design :

Results

Timeline

I found no difference when scrolling, Chrome DevTools timeline supports my perception : no difference, no fps drop.
The fact that the DOM is inline or in a proper css seems to not matter, I guess it’s already in the browser memory when it’s rendered so it does not influence the smoothness.

Size of the DOM

Without surprise, the DOM size is huge with the inline styles and is linearly proportional to the number of rows.

Type 1,000 rows 10,000 rows Ratio
CSS 517,725 chars ~5,177,250 chars x10
Inline 2,283,742 chars ~22,837,420 chars x10
x4.4 x4.4

That’s clearly something to take into account if you do some universal javascript. 2,000,000 chars is 2MB of data. (but who sends a big table anyway?!)

Time to mount the DOM

This is where componentDidMount is useful. We start a timer in the render method and end it when componentDidMount is called.
React has done its job converting the virtual DOM into a proper DOM and has injected it into our mounted node.

I let the times with the development version of React, because I started with and used the production version afterwards. They should not be taken into account, it’s not relevant in our case.

Type 1,000 rows 10,000 rows Ratio
CSS (debug) 650ms-750ms 6,000ms ~x10
Inline (debug) 1,000ms-1100ms 10,000ms ~x10
CSS 310ms 2,300ms ~x7.5
Inline 600ms 4,900ms ~x8
x2 x2.1

Yes, it takes more time to stringify our virtual DOM / mount it into the DOM.
The delta decreases for less rows but it’s still significant: 70ms (css) against 105ms (inline) for 100 rows (x1.5).

Rendering time

The DOM is mounted, the browser needs to render the table now. We can assume it will take longer to render the inline styles, because it has more to parse and store every style attribute of every td.

Type 1,000 rows 10,000 rows Ratio
CSS (debug) 1,100ms-1,200ms 9,500ms ~x10
Inline (debug) 1,500-1,600ms 14,000ms ~x10
CSS 720ms 6,000ms ~x8
Inline 1,080ms 9,400ms ~x8.7
x1.5 x1.56

Still a small overhead. I guess we could see some nice differences according to which browser is tested out!

What about another browser ? Edge !

I gave a try on Edge with 10,000 rows (React production version) :

Type mount time rendering time
CSS 4,115ms 11,591ms
Inline 11,418ms 21,113ms

It’s basically the double of Chrome times. :cry:

We knew that

So yes, we were kinda aware of all these behaviors, but now, we have some numbers !

This global analysis demonstrated that the inline styles take way more size and more time to be handled by the browser, but it has no more impact as soon as it’s rendered.

Compared to all the advantages the inline styles offer, we just have to be aware of those potential performance bottlenecks when thinking about the .

Further

Some plugins exists to extract the inline styles to css such as :
https://github.com/petehunt/jsxstyle : code inline then extract the constants to a stylesheet
https://github.com/js-next/react-style + https://github.com/js-next/react-style-webpack-plugin :

Or if you want the full power of css in javascript (media queries, pseudo-selectors, modifiers, and more!) :
Radium

Générer des classes css avec un nom unique

May 23rd, 2015 | es6, javascript, react, webpack |

A l’heure où l’on parle de micro-services, où on cherche à tout modulariser, et à créer des systèmes à base de dépendances explicites, à ne plus rien mettre dans le scope global et à utiliser le scope local uniquement, histoire de maîtriser ce à quoi on a accès et d’éviter des effets de bord : on utilise encore un moteur CSS où on balance tous les sélecteurs à sa racine, qui utilise donc un scope global. Heureusement, pour ceux qui utilise le shadow DOM, ce problème est résolu. Mais quid de ceux qui ne l’utilise pas ?

Une partie de ce problème peut être évité en adoptant des normes d’écriture et de nommage telle que la norme BEM qui peuvent malgré tout ne pas suffir ou être délicate à utiliser.

Enfin, surtout avec BEM, les noms de classes peuvent être à rallonge, on aimerait bien généraliser le process de minification qu’on utilise sur le contenu des .js et des .css, sur le nom des classes elles-même ! Mais cela oblige à modifier d’une part le nom de la classe dans le fichier .css et également le code js qui l’utilise.

Il est même maintenant possible de complétement se passer de fichier .css et d’utiliser uniquement du style inline (quid des performances ?). Par exemple, avec React, le style peut être défini directement dans les composants javascript, et avec des outils tel que Radium on peut même utiliser les sélecteurs spéciaux css tel que :hover, ou les média queries.

Facebook et le CSS

Facebook a déjà résolu ce problème. Sans doute avez-vous vu la présentation de  où il évoque comment Facebook s’assure qu’il n’y a aucun conflit de nom de classe css, que le scope global n’est pas pollué, et que les développeurs peuvent facilement rajouter du style sans avoir peur d’avoir des effets de bord et de modifier le layout quelque part, sans le savoir.

Ils ont étendu le langage css en rajoutant une syntaxe spéciale pour les sélecteurs : button/container qui ne peut être utilisé que dans le fichier button.css qui a son tour est référence dans un composant React button.js, qui enfin, fait référence à className={cx('button/container')} pour définir la classe d’un élément.

Le process de build vérifie ces références et génére un nom de classe unique à partir de button/container (qui n’est pas valide en CSS) par quelque-chose comme ._f8z qui fera parti du scope css global mais qui n’entrera jamais en conflit avec quoi que ce soit vu que le nom est généré aléatoirement (et est unique), personne ne pouvant le deviner à l’avance. Tout le monde doit donc utiliser ce système pour travailler et styler son contenu.

Moi aussi j’aimerai faire ça. Ca tombe bien, webpack est là.

webpack et css-loader

webpack, et en particulier le plugin css-loader combiné à extract-text-webpack-plugin, permet de former un (ou des) bundle css à partir de fichiers .css .less ou .sass (avec les loaders qui vont bien) qui sont eux-même importés dans des fichiers .js :

import React from 'react';
import './App.less';

export default class extends React.Component {
  render() {
    return 
} }

Avec une configuration webpack de ce genre :

var ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  entry: './src/app.js',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  loaders: [
     {
       test: /\.js$/,
       exclude: /node_modules/,
       loaders: [ 'babel-loader' ],
     },
     { // we can do: import './Component.css';
       test: /\.css$/,
       loader: ExtractTextPlugin.extract('style-loader', 'css-loader')
     },
     { // we can do: import './Component.less';
       test: /\.less$/,
       loader: ExtractTextPlugin.extract('style-loader', 'css-loader!less-loader')
     }
   ]
 },
 plugins: [
   new ExtractTextPlugin('[name].css')
 ]
};

Ce build permet de créer un bundle css qui contiendra tout le contenu référencé par les imports de fichiers .css ou .less dans les fichiers javascript.

import './App.less';

css-loader local scope

La nouveauté (d’avril 2015) est une nouvelle syntaxe au niveau des fichiers css pas encore transformés, mais prise en compte par css-loader :

:local(.container) {
  font-size: 30px;
}

Se transforme en :

._3ImWIJ65ktg-PxiyA_aFIC {
  font-size: 30px;
}

Cela signifie que vous ne pouvez plus utiliser simplement className="container", cette classe css n’existe plus. Et vous ne pouvez pas mettre className="_3ImWIJ65ktg-PxiyA_aFIC" quand même ! Il faut importer différemment le fichier .css ou .less :

import AppStyles from './App.less';
...

Les classes définies dans le fichier css seront accessibles via leur nom (si :local(.my-class) alors via my-class) dans l’objet importé, qu’on peut alors utiliser pour indiquer la className. Au niveau HTML, on aura ce rendu :

Autre exemple avec un autre sélecteur css à l’intérieur :

:local(.container) {
  font-size: 31px;
  span {
    letter-spacing: 5px;
  }
}

Cela génère bien :

._3ImWIJ65ktg-PxiyA_aFIC {
  font-size: 31px;
}
._3ImWIJ65ktg-PxiyA_aFIC span {
  letter-spacing: 5px;
}

Au niveau du bundle

AppStyles est en fait simplement un dictionnaire généré et injecté à la volée de la forme :

module.exports = {
  "container":"_3ImWIJ65ktg-PxiyA_aFIC"
};

className et pas style

Attention à bien utiliser className={ AppStyle.container } et pas style={ AppStyle.container } sans quoi l’erreur suivante se produirait :

Uncaught Error: Invariant Violation: The `style` prop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + 'em'}} when using JSX.

Le nom des classes générées

On peut modifier la manière dont le nom des classes sont générés. Par défaut, il s’agit d’un hash comme on peut voir, mais on peut le modifier de la sorte (via webpack.config.js) :

css-loader?localIdentName=[hash:base64]
_3ImWIJ65ktg-PxiyA_aFIC
css-loader?localIdentName=[path]-[name]-[local]-[hash:base64:5]
.src--App-container-3ImWI
  • path: le path du fichier javascript
  • name: le nom du fichier javascript
  • local: le nom utilisé dans le fichier css :local(.container)

Il n’y a malheureusement pas de méthodes (pour l’instant) pour générer des noms du genre ._a, ._b, … ._az histoire de rester ultra court et unique (étant un simple compteur).

La gestion de cet identifiant provient du module webpack loader-utils.

Plusieurs imports

Si un fichier Javascript dépend de plusieurs fichiers de styles et qu’un, il faut simplement concaténer le nom des classes provenant des 2 imports :

import AppStyle from './App.less';
import GlobalStyle from './Global.less';
...  
className={GlobalStyle.container + ' ' + AppStyle.container}

Plus loin

D’autres fonctionnalités sont disponibles, comme l’héritage de règles ou la définition de règle globale via :global, je vous conseille donc d’aller lire la page du projet : https://github.com/webpack/css-loader

Tout cela est encore récent et en développement (allant même à provoquer des breaking changes, des changements de syntaxe), ce qui la rend instable à utiliser pour l’instant, attention!

Docker sous Windows 8.1

May 17th, 2015 | docker |

Après avoir joué un peu avec boot2docker il y a quelques temps pour pouvoir tester Docker et lancer quelques hello-world, je suis tombé sur une news comme quoi une équipe avait réussi à *le* faire compiler sous Windows ! Il était possible de le récupérer via chocolatey et de l’installer simplement et magiquement à la Unix : choco install docker.

*Le* : comme *Le* client Docker, pas le moteur de conteneurs Docker qui lui, est encore exclusivement disponible sous un environnement Linux. Le client ne fait que se connecter à ce moteur. Sous Windows, il faut donc toujours une machine Linux quelque part faisant tourner le moteur de conteneur Docker : c’est ce quoi à sert boot2docker. Il installe en fait virtualbox et y créé une machine virtuelle :

Normalement, rien ne sert d’ouvrir VirtualBox pour jouer avec la VM. Les commandes de boot2docker s’en occupe:

   init                Create a new Boot2Docker VM.
   up|start|boot       Start VM from any states.
   ssh [ssh-command]   Login to VM via SSH.
   save|suspend        Suspend VM and save state to disk.
   down|stop|halt      Gracefully shutdown the VM.
   restart             Gracefully reboot the VM.
   poweroff            Forcefully power off the VM (may corrupt disk image).
   reset               Forcefully power cycle the VM (may corrupt disk image).
   delete|destroy      Delete Boot2Docker VM and its disk image.
   config|cfg          Show selected profile file settings.
   info                Display detailed information of VM.
   ip                  Display the IP address of the VM's Host-only network.
   shellinit           Display the shell commands to set up the Docker client.
   status              Display current state of VM.
   download            Download Boot2Docker ISO image.
   upgrade             Upgrade the Boot2Docker ISO image (restart if running).
   version             Display version information.

Les principales commandes étant : init (créer la VM), start/stop, ssh (se connecter à la VM pour avoir accès à son shell), upgrade (à faire de temps en temps pour mettre à jour la VM si mise à jour il y a).

Pour rappel, installer le client de chocolatey n’est pas requis pour utiliser Docker sous Windows. Il y a déjà un client Docker dans la VM installé par boot2docker. Il suffit de s’y connecter via boot2docker ssh et d’utiliser les commandes $ docker run hello-world.
Le client de chocolatey permet de se passer de cette étape, et d’utiliser les commandes docker directement sous votre shell Windows (cmd, powershell, cmder..).

Client docker => docker engine (boot2docker VM)

On commence par s’assurer que la VM de boot2docker est en train de tourner.

> boot2docker up
Waiting for VM and Docker daemon to start...
............................ooooooooooooooooooooooo
Started.
Writing C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm\ca.pem
Writing C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm\cert.pem
Writing C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm\key.pem

To connect the Docker client to the Docker daemon, please set:
    export DOCKER_HOST=tcp://
    export DOCKER_CERT_PATH='C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm'
    export DOCKER_TLS_VERIFY=1

Une fois up, il vous exécuter les lignes indiquées pour que le client Chocolatey puisse se connecter au daemon Docker sur la VM.

Si vous n’exporter pas ces variables, vous rencontrez ce genre d’erreur :

> docker run hello-world
FATA[0000] Post http:///var/run/docker.sock/v1.18/containers/create: dial unix /var/run/docker.sock: An address incompatible with the requested protocol was used.. Are you trying to connect to a TLS-enabled daemon without TLS?

En gros, cela permet au client Docker de savoir où est la VM via son IP et où est le certificat pour s’y connecter via TLS.
Vous pouvez revoir ces variables via la commande shellinit :

> boot2docker shellinit
Writing C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm\ca.pem
Writing C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm\cert.pem
Writing C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm\key.pem
    export DOCKER_HOST=tcp://
    export DOCKER_CERT_PATH='C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm'
    export DOCKER_TLS_VERIFY=1

En mode Windows, cela donne :

> set DOCKER_HOST=tcp://
> set DOCKER_CERT_PATH=C:\Users\Cthulhu\.boot2docker\certs\boot2docker-vm
> set DOCKER_TLS_VERIFY=1

Une fois cela fait, un petit test histoire de voir que tout va bien :

> docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from hello-world

a8219747be10: Pull complete ================================================>]    596 B/596 BB
91c95931e552: Already exists ===============================================>]     32 B/32 BB
hello-world:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.

Digest: sha256:aa03e5d0d5553b4c3473e89c8619cf79df368babd18681cf5daeb82aab55838d
Status: Downloaded newer image for hello-world:latest
Hello from Docker.
This message shows that your installation appears to be working correctly
...

N’oubliez pas de donner une valeur à ces variables au démarrage de votre shell à chaque fois, via un .bashrc si vous avez, ou via les variables d’environnement Windows :

Les erreurs possibles

Si, quand vous lancer la VM dans VirtualBox, si vous obtenez cette erreur : VT-x is not available (VERR_VMX_NO_VMX)

Ou si en utilisant boot2docker, vous obtenez, peu importe la commande, cette erreur :

error in run: Failed to start machine "boot2docker-vm": exit status 1

Vérifiez que Hyper-V ne soit PAS activé dans vos Windows Features, sinon désactivez-le, et redémarrer votre Windows pour que cela soit pris en compte.

Plus rarement, car toujours activé de nos jours, et que je n’ai pas testé, j’ai également lu qu’il faut s’assurer que la virtualization  VT-x soit activée dans votre BIOS (généralement dans un menu Advanced > CPU).


 

Si vous avez des erreurs bizarre comme quoi la VM n’existe pas alors qu’elle est bien là :

> boot2docker -v ssh
Boot2Docker-cli version: v1.6.2
Git commit: cb2c3bc
2015/05/17 23:01:24 executing: C:\Program Files\Oracle\VirtualBox\VBoxManage.exe showvminfo boot2docker-vm --machinereadable
error in run: Failed to get machine "boot2docker-vm": machine does not exist (Did you run `boot2docker init`?)

> boot2docker init
error in run: Failed to initialize machine "boot2docker-vm": exit status 1

Pourtant, la VM est bien là !

> ls "VirtualBox VMs\boot2docker-vm\
Logs boot2docker-vm.vbox boot2docker-vm.vbox-prev boot2docker-vm.vmdk

N’oubliez pas de lancer votre shell ou votre gestionnaire VirtualBox en mode administrateur ! Testé et approuvé.

Using react-hot-loader with a webpack-dev-server and a node server

May 14th, 2015 | expressjs, hot reloading, nodejs, react, webpack |

2015-12-31: You should check out my recent post to use the new way of doing HR with webpack, and to have way more details :
Webpack Hot Reloading and React : how ?

Don’t work without hot-reloading

I got a project running its own Node server instance, using expressjs. It’s already bundling its assets with webpack, and it’s using React with the nice ES6 features, all good.

But it lacks of hot reloading. :-(

This is something we must have nowadays when you’re working with webapps and CSS/Javascript. You don’t want to refresh your whole page and lose your current state each time, you are going to lose your time, your patience, and your productivity !

Leave express alone and add webpack-dev-server on top

Thus, I decided to take a look and tried to add it while keeping the express server instance up and working. I didn’t want to replace the whole thing by a webpack-dev-server and reconfigure the public assets paths and all other stuff that my expressjs was providing.

After some tests, some readings here and there, I got a pretty straightforward solution without using any custom proxy code, nor changing my generated html.

The solution

For reference:
– the expressjs server is running on localhost:3000
– the webpack-dev-server is going to run on localhost:3001

Here is a git diff of my changes:

packages.json

+ "react-hot-loader": "^1.2.7",
+ "webpack-dev-server": "^1.8.2"

Just the necessary packages to do the hot reloading. (npm install --save-dev)

webpack.config.js

+var webpack = require('webpack');

- entry: path.resolve(__dirname, '../src/client.js'),
+ entry: [
+   'webpack-dev-server/client?http://0.0.0.0:3001',
+   'webpack/hot/only-dev-server',
+   path.resolve(__dirname, '../src/client.js')
+ ],

modules: {
  loaders: [
    {
-     loader: 'babel-loader'
+     loaders: [ 'react-hot-loader', 'babel-loader' ]
    }
  ]
},

+ plugins: [
+   new webpack.HotModuleReplacementPlugin(),
+   new webpack.NoErrorsPlugin()
+ ]

– we add webpack-dev-server entries into entry
– we add the react-hot-loader loader when processing the .js (it will add some js to magically do the hot reloading)
– we add the plugins for hot reloading.

More details here http://gaearon.github.io/react-hot-loader/getstarted/

server.js

This is the most important part, where we define a new webpack-dev-server instance that will be listen by the browser to process the hot reloading if any js file change.

// existing express server
var app = express();
app.use(...)
app.get(...)
app.listen(3000);

+// we start a webpack-dev-server with our config
+var webpack = require('webpack');
+var WebpackDevServer = require('webpack-dev-server');
+var config = require('./webpack.config.js');

+new WebpackDevServer(webpack(config), {
+   hot: true,
+   historyApiFallback: true,
+   proxy: {
+     "*": "http://localhost:3000"
+   }
+}).listen(3001, 'localhost', function (err, result) {
+   if (err) {
+     console.log(err);
+   }
+
+   console.log('Listening at localhost:3001');
+});

We are only adding a new webpack-dev-server instance without altering at all our expressjs server listening on localhost:3000.

– our webpack-dev-server listens to localhost:3001 and handle the hotness
– every requests (“*”) are redirected to localhost:3000.

This server serves only as a proxy for the hot reloading. More details here http://webpack.github.io/docs/webpack-dev-server.html.

At the end, there is still no hot reloading on http://localhost:3000 (it’s still a pure expressjs server).

But if you browse http://localhost:3001, and more precisely http://localhost:3001/webpack-dev-server/, you will the famous “App ready” hot reloading toolbar and everything will be automatically updated if you change one of your React component.

Then, suddenly

May 9th, 2015 | Uncategorized |

It’s been a while.

I couldn’t sleep. I was thinking, maybe too much.

bookmarks.findAll();

I love IT, I love new technos, I love to discover new things and try to use them in the best way possible. I love to read stuff, blogs, twitter, I follow every links I find and read about everything. I want to know everything.

I have something like 100 unread bookmarks (I’m reading a bit every day but finding new ones at the same time), and the export file of all of them stands at 1800 lines, wow.

time.stop();

Discussing last night with a friend of mine who is quite successful — he’s working on different interesting projects on its own or with a team, knows a lot of tools/technos/languages, use them properly in a performant way, knows what he wants, and how to evaluate the stuff he’s working with —  made me realize that I want the same, but I’m stuck.

I’m not more stupid than he is, why couldn’t I ? what is blocking me ?

I’ve started BASIC at 10 and has always been some kind of reference for my friends/collegues when it comes to languages, patterns, ideas.

Thus this long night, then this post. Why should I post something and not move on, right now ? I guess it’s just to recap my own thinking.

require(‘experiment’);

So I’m reading a lot, and as I was tolding him, I can’t practice everything I read, there is way too much.

I already barely have time to read what I want and bookmark new stuff, and do things with my wife, how could I experiment those new tools, technos, languages I’m reading about ?

For some of them, I could, because it’s not that complicated to use at first glance such as the Javascript framework and tools. You don’t need a lot except NodeJS most of the time, install some modules and you can give a try.

When it comes to tools, that’s sometimes more difficult. For instance, I didn’t test “Flow” yet (the static typechecker from facebook) because I’m running on Windows and there is no official binaries yet. When you don’t have a Mac or a Linux, that can slow you down in your progress. (oh and I have a Windows Phone since few years, I’m definitely thinking to abandon Microsoft there, I can’t support to never have the application I want. It’s often Apple Store only).

I didn’t test every database or server I’m reading about. I don’t have time ! Or I just realize that it’s just useless to install something, do the Hello World, then be done. That’s useless, and I will forget about it in a few days.

If I don’t have a project using it, I just don’t see the point.

I just want to remember the name, what it does, what is its strength, “in case of”.

But I realize that I often forget things I’ve read one month ago if I didn’t practice. Even with you experiment something, you come there one month later, I’m way more interrogative about what should I type, where should I go, and always need to refer to Google or my bookmarks, or lose time trying stuff until I find the good combinaison.

But sometimes, things stays, such Sublime Text. It was a awesome discovery few months ago and I’m still using it. I’m not mastering every shortcuts, just the one I need and can think of, I know there are a bunch of them that could often help me, it’s just that I don’t realize. I guess with the experience, that will come in handy.

Few month ago, I watched a video of a guy coding an algorithm (in NodeJS, doing TDD). I was amazed by the speed of the development and the tools he was using : build the skeleton using yo, quite fast at using VIM with a lot of shortcuts, install what was needed to run the tests, auto-run them, then finally writing the algorithm etc. For some, this can seems like “why the fuck should I watch a random guy coding”, well, it was actually very very interesting to see how someone else is doing, you can learn so much.

We need you !

  • I want to do some big data work with Hadoop, Cassandra, HBase, CouchDB.
  • I want to run some true queue tasks system such as ZeroMQ, Apache Apollo, RabbitMQ.
  • I want to use some virtual machines and containers (in a performant, secure, reliable and proper way! not just install/run and be done!).
  • I want to use some scalable systems, distributed databases, see how it reacts.
  • I want to use graph database such as Neo4J and do some ElasticSearch.
  • I want to work on a isomorphic React website using ES6.
  • I want to get rid of WordPress and write my own performant React blog with multilanguage support.
  • I want to use Redis, I barely test it and I know its potential is huge.
  • I want to use Varnish, HAProxy, I know they are the way to go for web performance.
  • I want to use admin and deployment tools such as Vagrant, Chef, Puppet, Ansible.
  • I want to use the cloud as it should (Amazon, Azure), never did.
  • I want to work using GitHub with a team, and work the proper way (branches, rebase).
  • I want to use Slack
  • I want to work with nice work and build processes (hot reloading, gulp/webpack).
  • I want to do some mobile native app (and React Native ofc).
  • I want to work with TDD, I don’t like bugs.
  • I want to work with performant tools, and be able to profile anything. I don’t like to wait, and I love performances.

I want to masterize some tools. I don’t masterize anything right now. I know “well” React, I’ve subcribed to the github repo and I’m reading every emails I got but I’m starting to feel that it’s just useless. I’ve started some side projects but never finished them. At work, I’m using the classic .NET framework, and basic SQL, no fancy stuff. I know them “well” but that’s it. I think I’m getting lazy of all that. I want to work on new stuff, new tools, new ways of thinking.

volatile-ttl

Doing a introspection of myself, I realize that :

  • my memory sucks
  • if I don’t work on something during some times, I will forget about it. (well, it will go into my bookmarks at least!)
  • I don’t know how to evaluate if a tool or framework is good
  • I’m interesting in everything, I should filter what I know I won’t use
  • I know of lot of names (tools, frameworks) but can’t
  • And still missing important ones (I just heard about Apache Apollo, Jenkins)
  • I’m not mastering anything
  • I love to learn stuff but don’t practise enough
  • I like to write
  • I like order, performance, consistency, clean stuff
  • I like money, I want passive income
  • I can’t stay with one tool/one framework, I’m always attracted with the latest and new one
  • I don’t have a lot of free time: need to optimize how I’m thinking, how I’m working
  • I should unsubscribe from React mailing list
  • I should truly install Linux or well, buy a MacBook Pro and the iPhone with it.

Okay, this post is a bit messy but this is what was on my mind.

Now, listening to Global Communication to relax (thanks to some reddit post I stumbled upon!).