Reduce Angular bundle size by remove unused locale in moment.js (without ng eject)

This is my lab test for remove a moment locale from angular app. This article will show you how to reduce Angular bundled size by remove unused locale in moment.js without ng eject

WARNING: This is my first english article. I’m not good english but i think this should be share to other people.

Prepare a lab data

Just install Angular 7 and moment by this command.

ng new ng7-moment
cd ng7-moment
npm install --save moment

And edit `app.component.ts` to something like this

import { Component, OnInit } from '@angular/core';
import * as moment from 'moment';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  time: string;

  ngOnInit() {
    this.time = moment().format('MMMM Do YYYY, h:mm:ss a');
  }
}

And build a production bundle with stats json. by

npm run build -- --prod --stats-json

Next i see how many size of moment.js that include to bundle by

webpack-bundle-analyzer dist/ng7-moment/stats.json

And this i a result

Angular bundle with a full locale of moment.js

As you can see total size is around 300KB (Gzipped around 70KB).

First try: Import min edition

After I google for solution i found someone suggest that I can import moment.min.js. That file not include any locale data. So i change code to

import { Component, OnInit } from '@angular/core';
import * as moment from 'moment/min/moment.min.js';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  time: string;

  ngOnInit() {
    this.time = moment().format('MMMM Do YYYY, h:mm:ss a');
  }
}

The bundle size after changed is

Bundle size after import min version of moment.js

Now size is reduce to around 51KB (Gzipped around 16KB). WOW! At first time I think it works. But when I import a locale data like this code.

import { Component, OnInit } from '@angular/core';
import * as moment from 'moment/min/moment.min.js';
import 'moment/locale/th';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  time: string;

  ngOnInit() {
    this.time = moment().format('MMMM Do YYYY, h:mm:ss a');
  }
}

And see a bundled size.

bundle size when import min version of moment.js and import localeAs you see we have 2 moment.js and full locale in bundled (one is full version and another one is min version). And if you import another library that use moment.js like chart.js the size will be like that too.

bundle size after import min version of moment.js and import chart.js

Second try: Use webpack plugins

I search in Google again. I see another solution to eject webpack config and add some plugin to remove locale. But for my personal reason i don’t want to eject angular config, So I will skip this solution for now.

Solution: Remove by Replace locale folders.

I found Angular has a feature that can replace environments file up to version that it build. I think I can use this folder to replace locale folder too. So let try

  1. Create a my locale folder and if you use any locale, copy it to this folder.
    mkdir src/locale
    
    # If you want to use any locale copy it to your locale folder
    cp node_modules/moment/locale/th.js src/locale/
  2. Edit angular.json to replace default locale. file fileReplacements section and add this code like this.
                  "fileReplacements": [
                    {
                      "replace": "src/environments/environment.ts",
                      "with": "src/environments/environment.prod.ts"
                    },
                    {
                      "replace": "node_modules/moment/locale/",
                      "with": "src/locale/"
                    }
                  ],
  3. Build and see a results. (You will see a warning message like “node_modules/moment/locale/af.js” does not exist. But it OK don’t worry it)

The result is

bundle size after remove unused locale.As you can see the size is around 55KB (Gzipped around 17KB) and no other locale except locale that in new locale folder. even if you import chart.js it still not include unused locale too. Like this

Bundle size after remove unused locale and import chart.js

I hope this will be useful. Thanks for reading.

เพิ่ม Index ให้ WordPress เพื่อลดโหลด

อันนี้เป็นบทความสั้นๆครับ เกิดจากการสังเกตว่าใน WordPress ของผมนั้นจะมี Query อันนึงที่ run เยอะมาก และบางทีก็นานมาก ตัวอย่างประมาณนี้ครับ

SELECT post_id FROM wp_postmeta WHERE meta_key = '_wp_attached_file' AND meta_value = '2015/11/top-5-antivirus.jpg';

ซึ่งจากที่เดาคือมันเช็คว่า Attachment ตัวนี้มี Post ไหนใช้บ้าง ผมไม่รู้ว่าตัวไหนเป็นคนเรียกใช้ แต่เนื่องจากมันเยอะมาก เลยทำการเพิ่ม index ให้มัน ทำให้มัน Query ได้เร็วขึ้น

ALTER TABLE wp_postmeta ADD INDEX first_50_key_10_value (meta_key(50),meta_value(10))

เพื่อความประหยัดผมเลย index meta_value แค่ 10 ตัวพอ (เพียงพอสำหรับ /ปี/เดือน/ ก็ลดไปได้เยอะและ)

แต่อันนี้ไม่ได้ช่วยกับทุกคนนะครับ บางทีมันอาจจะมาจาก Theme หรือ Plugin ก็ได้ ดังนั้นต่อให้ท่านใช้ไปก็ไม่อาจจะรับประกันได้ว่ามันจะช่วยท่านได้

Page File คืออะไร? เราควรปิดหรือเปิดมันดี? ควรใช้ RAM Disk เก็บไหม?

Page File คืออะไร

Page File (บางคนเรียก Swap file คล้ายๆกัน) คือไฟล์ที่ระบบ OS ต่างๆ เช่น Windows สร้างขึ้นมาเพื่อย้ายข้อมูลบางส่วนใน RAM มาเก็บใน Hard Disk ถ้าหากถามว่ามันทำเพื่ออะไร Hard Disk มันช้ากว่า RAM ไม่ใช่เหรอ คำตอบง่ายๆ เพื่อไว้ใช้ในกรณีที่มีโปรแกรมใช้ RAM มากๆ จนทำให้ RAM ไม่พอ จึงต้องย้ายบางโปรแกรมที่ไม่ใช้ไปเก็บที่อื่นก่อน เพื่อให้ RAM พอกับโปรแกรมที่เราใช้ในปัจจุบัน (ในบางครั้ง มันก็ย้ายลง Page file ทั้งที่ RAM ยังเหลืออยู่ เพราะว่ามันเก็บ Cache การอ่านไฟล์ลง RAM ด้วย ยกตัวอย่างเช่น การเปิด Word ครั้งแรกคุณจะรู้สึกว่าช้า แต่เมื่อคุณเปิดอีกครั้งมันจะเร็วขึ้น เพราะข้อมูลบางส่วนถูก Cache อยู่ใน RAM)

แล้วทำไมบางคนบอกให้ไปปิดมัน

อย่างที่ทราบกันว่า Hard Disk นั้นช้ากว่า RAM มาก เมื่อมีการย้ายข้อมูลบางส่วนลง Hard Disk ไปใส่ Page file แล้ว เมื่อมีการเรียกใช้ข้อมูลส่วนนั้น มันก็จะต้องวิ่งผ่าน Hard Disk ซึ่งช้ากว่า ทำให้มันช้าลงในกรณีนี้

แล้วจริงๆแล้วเราควรปิดมันไหม

อันนี้ไม่มีคำตอบตายตัว ต้องพิจารณาหลายอย่าง เช่น RAM ที่เครื่องมี โปรแกรมที่ใช้ โปรแกรมที่ถูกโหลดตอนเครื่องเปิด เป็นต้น

ยกตัวอย่างเช่น เครื่องผมมี RAM 16 GB และไม่ได้ใช้โปรแกรมที่กิน RAM สูงอย่างพวก Photoshop โดยทั่วไปผมใช้แค่ Word, PowerPoint,Chrome,Firefox ทั่วๆไป มีการเขียนโปรแกรมใช้พวก Eclipse, phpStorm ซึ่งกิน RAM ค่อนข้างมาก แต่ไม่เกิน 4GB ดังนั้นโดยทั่วๆไปเครื่องผมไม่น่าจะใช้ RAM ถึง 8 GB ได้ ดังนั้นในกรณีผมควรจะปิด เพื่อให้ใช้เครื่องได้ลื่นไหลขึ้น  (ไม่มีการเก็บข้อมูลใน Hard Disk แน่นอน เลยไม่มีการหน่วงเวลาสลับโปรแกรม)

อันนี้ก็ขึ้นกับ RAM ของคุณและพฤติกรรมของคุณ ถ้าคุณใช้แค่ Chrome, Firefox,Word ทั่วไป และมี RAM มากกว่า 8GB ผมแนะนำว่าควรปิดไปเลยก็ได้

คำเตือน หากคุณมีการใช้ Photoshop ตัอต่อ VDO หรืออะไรก็ตามที่ใช้ RAM เยอะ ไม่ควรปิดโดยเด็ดขาด เพราะหาก RAM ไม่พอ ถ้าโชคดีมันเตือนทัน มันก็จะเตือนให้ไปปิดโปรแกรมอื่น หรือเปิด Page file ก่อน แต่ถ้าเราแก้ไขไม่ทัน ตัว OS จะปิดโปรแกรมนั้นทิ้งทันที ซึ่งอาจจะทำให้ข้อมูลคุณหายได้

วิธีปิด Page file

หลังจากที่คุณตัดสินใจจะปิด Page file แล้ว การปิด Page file มีขั้นตอนดังนี้

  1. กดปุ่มรูป Windows + R แล้วพิมพ์ SystemPropertiesAdvanced ลงไป กด OK
    เปิด Run Command และใส่ SystemPropertiesAdvanced ลงไป กด OK
  2. กดที่ Settings
    กดที่ Settings
  3. ไปที่ Advanced และกด Change
    ไปที่ Advanced และกด Change
  4. เอาเครื่องหมายถูกบนสุดออก เพื่อบอกว่าเราจะตั้งค่า Page File เอง และเลือก No Paging File และกด Set เพื่อปิด Page file มันจะขึ้นข้อความถามว่า จะปิด Page file จริงๆใช่ไหม ให้กด Yes เลย
    เอาเครื่องหมายถูกบนสุดออกและเลือก No Paging File
  5. กด OK เรื่อยๆจนหมดครับ มันจะบอกว่าให้ Restart เครื่องไหม ให้กด Restart Now เพื่อ Restart ครับ เพื่อให้เครื่องปรับการตั้งค่า (ไม่สามารถลัดด้วยการ kill explorer.exe เปิดใหม่ได้นะครับ)

หากต้องการใช้ Page file ควรคิดถึงอะไรบ้าง

หากยังต้องการเปิด Page file อยู่ ไม่ว่าด้วยเหตุผล RAM ไม่เยอะ หรือป้องกันโปรแกรมปิดเองเวลา RAM ไม่พอ ผมมีข้อแนะนำนิดหน่อย ดังนี้

  1. เราควรเลือกให้อยู่ใน Partition ที่อยู่ Hard Disk คนละตัวกับที่มี Windows ถามว่าทำไม เพราะโปรแกรมโดยมากใช้ข้อมูลบน โฟลเดอร์ Windows และ Program files ในนั้น ดังนั้นหากเราย้ายไปที่ Hard Disk คนละตัว จะช่วยให้สามารถอ่านเขียนได้เร็วขึ้น เพราะไม่ต้องแย่งทรัพยากรกับโปรแกรมอื่น
  2. หากมี Hard Disk แค่ตัวเดียว ควรเลือก Partition ที่มีที่ว่างเหลือเยอะที่สุด เพื่อที่จะทำให้ Page file จะอยู่ติดกัน ไม่มี fragment บน Hard Disk มากนัก จะช่วยให้เร็วขึ้นนิดหน่อย
  3. ไม่ควรใส่ Page file ลงบน SSD เพราะ ถึงแม้ SSD จะเร็วมาก แต่มีการจำกัดจำนวนครั้งที่จะเขียนได้ต่อลูก ดังนั้นจึงทำให้อายุของ SSD สั้นลงได้ จนถึงขั้นว่าบางคนแนะนำให้ปิด Page file ถ้ามี SSD แค่ลูกเดียว

เราควรใช้โปรแกรมพวก RAM Disk ช่วยไหม

ผมไปอ่านมาหลายที่ มีหลายคนชอบแนะนำว่าให้ใช้โปรแกรม RAM Disk ช่วย คำถาม RAM Disk คืออะไร มันเป็นโปรแกรมที่นำพื้นที่บางส่วนของ RAM มาใช้เก็บข้อมูล และเนื่องจาก RAM มันเร็วมาก บางคนเลยเอามาใส่ Page File ซะเลย ซึ่งเป็นเรื่องที่ผิดครับ

ถามว่าทำไม? ลองกลับไปอ่านต้นๆของบทความนะครับ Page file คือ file ที่เก็บบางส่วนของ RAM ไปไว้บนไฟล์ เพื่อช่วยให้ระบบทำงานลื่นไหล แม้ว่าโปรแกรมจะกิน RAM มหาศาลก็ตาม

ทีนี้เมื่อเราใช้ RAM Disk ซึ่งเอาบางส่วนของ RAM มาทำเป็น Partition ดังนั้นหมายความว่า RAM Disk ไม่มีทางสร้างได้มากกว่าขนาดของ RAM ที่เรามี ดังนั้นจึงหมายความว่า Page file จะไร้ค่าไปในทันที เพราะโดยรวมแล้วจะไม่สามารถย้ายข้อมูลที่ไม่ใช้ออกนอก RAM ได้เลย เพราะงั้นถ้าจะใส่ Page file ลง RAM Disk ปิด Page file ไปเลยก็ได้ครับ

 

 

WordPress ช้าเพราะ Really Simple CAPTCHA

อันนี้เอามาจากโพสนี้ ไม่ได้เจอเอง คิดว่าน่าจะแก้ได้แล้วเพราะเจ้าของโพสมากดไลค์ แต่ไม่แจ้งผล

really_simple_captcha_property

ปัญหาที่เกิดคือเค้าบอกว่าเว็บช้ามาก เพราะ Really Simple CAPTCHA และขนาดของ plugin ใหญ่มาก (พวก cache ภาพที่ gen แล้วต่างๆ) ตอนแรกผมไม่เชื่อเท่าไหร่ เพราะผมคิดว่ามันไม่น่าจะช้าได้จากตัวนี้ จนกระทั่ง เค้าใช้ P3 Profiler มาให้ดูจริงๆ ผมก็เลยไปลองไล่ดู Code

ผมเดาว่าสาเหตุนั้นมาจากตอน clean up พวกภาพที่หมดอายุต่างๆ ทำให้มันกิน resource จากตัวอย่างคือ loop อ่าน meta file 20000 กว่ารอบ เพื่อลบไฟล์ที่ไม่ใช้ (ยิ่งถ้าใช้พวก NFS นะ โคตรช้า)

Solution

  1. เปิดไฟล์ really-simple-captcha.php หา function cleanup ให้แก้ไขเป็นดังนี้
    	public function cleanup( $minutes = 60 ) {
    		return 0;
    	}

    (ปิดการทำงานของ clean up เพื่อไม่ให้เว็บช้า เราจะย้ายไปทำใน background แทน)

  2. ให้สร้างไฟล์ชื่อ cleanup.php ข้างๆ really-simple-captcha.php มีเนื้อความดังนี้
    <?php
    $minutes=60;
    $dir = trailingslashit( path_join( dirname( __FILE__ ), 'tmp' ) );
    $dir = str_replace( '\\', '/', $dir );
    $dir = preg_replace( '|/+|', '/', $dir );
     
    if ( ! @is_dir( $dir ) || ! @is_readable( $dir ) )
            return false;
     
    $is_win = ( 'WIN' === strtoupper( substr( PHP_OS, 0, 3 ) ) );
     
    if ( ! ( $is_win ? win_is_writable( $dir ) : @is_writable( $dir ) ) )
            return false;
     
    $count = 0;
     
    if ( $handle = @opendir( $dir ) ) {
            while ( false !== ( $filename = readdir( $handle ) ) ) {
                    if ( ! preg_match( '/^[0-9]+\.(php|txt|png|gif|jpeg)$/', $filename ) )
                            continue;
     
                    $file =  $dir . $filename ;
                    $file = str_replace( '\\', '/', $dir );
                    $file = preg_replace( '|/+|', '/', $dir );
     
                    $stat = @stat( $file );
                    if ( ( $stat['mtime'] + $minutes * 60 ) < time() ) {
                            @unlink( $file );
                            $count += 1;
                    }
            }
     
            closedir( $handle );
    }
     
    ?>

    (เป็นไฟล์สำหรับ clean up แบบ manual)

  3. สร้าง cronjob เพื่อสั่งให้ cleanup.php ทำงาน (หรือเข้ามาสั่งเองทุกวันก็ได้ แต่ถ้าสั่งช้า มันจะกินพื้นที่ HDD เยอะขึ้นเรื่อยๆ)

จบ เอาไปทดสอบได้เลยครับ

ติดตั้ง/ปรับแต่ง DirectAdmin เพื่อให้รองรับจำนวนคนจำนวนมาก

อันนี้เป็นประสบการณ์กากๆของผมที่ทำต้องจัดการให้เซิฟเวอร์ที่ใช้ DirectAdmin อยู่ สามารถรองรับจำนวนคนได้เยอะๆครับ วิธีการง่ายๆดังนี้ครับ

  1. ปรับ Web Server ให้ใช้ Nginx เป็น Reverse Proxy (อ้างอิงวิธีการจาก ลิงค์นี้)
    1. ก่อนอื่นสำหรับ คนที่ติดตั้ง DirectAdmin ไปแล้วต้องมั่นใจว่า CustomBuild ต้องมากกว่าเวอร์ชั่น 2 สามารถตรวจสอบได้โดย SSH เข้าไป แล้วใช้คำสั่งดังนี้
      cd /usr/local/directadmin/custombuild
      ./build version

      ดูว่าเลขหน้าสุดเป็นเลข 2 ไหมครับ ถ้าไม่ ให้ทำการปรับ CustomBuild เป็นเวอร์ชั่น 2 ครับ โดยสามารถทำได้ตามลิงค์นี้ครับ หรือสั่งคำสั่งด้านล่างนี้ครับ (คำเตือน Backup ทุกอย่างก่อน Update ทุกครั้ง)

      cd /usr/local/directadmin
      mv custombuild custombuild_1.x
      wget -O custombuild.tar.gz http://files.directadmin.com/services/custombuild/2.0/custombuild.tar.gz
      tar xvzf custombuild.tar.gz
      cd custombuild
      ./build
      ./build all d
      ./build rewrite_confs
    2. ติดตั้ง Nginx เป็น Reversed Proxy ซึ่งตรงนี้ทาง DirectAdmin เริ่ม Support การติดตั้งอัตโนมัติแล้ว สามารถทำได้ตามคำสั่งด้านล่างนี้ครับ
      cd /usr/local/directamin/custombuild
      ./build update
      ./build update_da
      ./build set webserver nginx_apache
      ./build nginx_apache
      ./build rewrite_confs
    3. เมื่อติดตั้งเสร็จแล้ว มันยังมีปัญหานิดหน่อยครับ คือตัว Apache ทีให้บริการ Web App นั้นมอง IP ของเซิฟเวอร์เองเป็น Client เนื่องจากวิ่งผ่าน Nginx จึงต้องลง mod เพิ่ม ดังนี้ครับ
      cd /usr/local/src
      wget https://github.com/y-ken/mod_rpaf/archive/master.zip
      unzip master.zip
      cd mod_rpaf-master
      /bin/sed -i "s/remote_/client_/g" mod_rpaf-2.0.c
      make && make install

      จากนั้นไปที่ /etc/httpd/conf/extra/httpd-includes.conf  แล้วเพิ่มข้อความต่อไปนี้ลงล่างสุดครับ

      LoadModule rpaf_module      /usr/lib/apache/mod_rpaf-2.0.so
      <IfModule mod_rpaf-2.0.c>
          RPAFenable On
          RPAFproxy_ips 127.0.0.1
          RPAFsethostname On
          RPAFheader X-Client-IP
      </IfModule>
  2. ปรับแต่งเซิฟเวอร์เพื่อให้รองรับคนจำนวนมากได้ (จำเป็นค่อนข้างมาก ไม่งั้นอาจจะเจอ Bad Gateway หรือ Gateway Timeout บ่อย)
    1. ปรับแต่ง Kernel Queue เพื่อให้รับการเชื่อมต่อจำนวนมากๆได้ โดยใช้คำสั่งต่อไปนี้
      sysctl -w net.core.somaxconn=100000

      และเพิ่มบรรทัดต่อไปนี้ลงในไฟล์ /etc/sysctl.conf

      net.core.somaxconn=100000
    2. ปรับแต่ง Port ที่ระบบใช้ได้ให้มากกว่าเดิม โดยใช้คำสั่งต่อไปนี้
      sysctl -w net.ipv4.ip_local_port_range="10000 65535"

      และเพิ่มบรรทัดต่อไปนี้ลงในไฟล์ /etc/sysctl.conf

      net.ipv4.ip_local_port_range=10000 65535
    3. ปรับแต่งให้ปิดการเชื่อมต่อได้เร็วขึ้น เพื่อเอาไปใช้รับการเชื่อมต่อใหม่ โดยใช้คำสั่งต่อไปนี้
      sysctl -w net.ipv4.tcp_tw_reuse=1

      และเพิ่มบรรทัดต่อไปนี้ลงในไฟล์ /etc/sysctl.conf

      net.ipv4.tcp_tw_reuse=1
    4. อันนี้ผมจำไม่ได้ว่ามันทำอะไรครับ แต่เห็นเซิฟเวอร์ปัจจุบันมี และคิดว่าค่อนข้างสำคัญ
      sysctl -w net.ipv4.ip_nonlocal_bind=1

      และเพิ่มบรรทัดต่อไปนี้ลงในไฟล์ /etc/sysctl.conf

      net.ipv4.ip_nonlocal_bind=1
    5. ปรับแต่งเพิ่มจำนวนไฟล์สูงสุดที่ 1 โปรเซสเปิดได้ให้มากที่สุด โดยใช้คำสั่งต่อไปนี้
      ulimit -n 999999

      และเพิ่มบรรทัดต่อไปนี้ลงในไฟล์ /etc/security/limits.conf

      * soft nofile 999999
      * hard nofile 999999
  3. ปรับแต่งให้ Nginx ให้บริการ Static File (ไฟล์ที่ไม่ประมวลผลเช่นภาพเป็นต้น) โดยไม่ผ่าน Apache เพราะ Nginx สามารถทำงานด้านนี้ได้ดีกว่า Apache มาก (คำเตือน วิธีนี้จะทำให้ส่วนที่ถูกซ่อนไว้ โดย Password Protected Directories ของ DirectAdmin อาจจะทำให้ข้อมูลที่ถูกซ่อนบางส่วนหลุดไปได้โดยไม่ถามรหัสผ่าน แต่ผมว่าในเคสทั่วไปไม่ค่อยมีคนใช้) โดยสั่งคำสั่งดังต่อไปนี้
    cd /usr/local/directadmin/data/templates/custom
    wget "https://www.ishare.in.th/wp-content/uploads/2015/02/conf.zip"
    unzip conf.zip
    rm -f conf.zip
    cd /usr/local/directadmin/custombuild
    ./build rewrite_confs
    service nginx reload

    เพียงเท่านี้ Nginx ก็จะให้บริการ Static File ส่วนมากให้ทันที (ผมอาจจะ list ไม่หมดเพราะผมก็ไปคัดลอกจากที่อื่นมาครับ)

เพียงเท่านี้ก็คิดว่าเซิฟเวอร์ปัจจุบันของท่านก็น่าจะรับโหลดเพิ่มขึ้นได้อีกเยอะครับ หากยังไม่สามารถรองรับได้อีก อาจจะต้องปรับ nginx ให้ cache หน้าที่ประมวลผลไว้สักหน้าละ 10 – 60 วิ แล้วแต่กรณีไป แต่พอดีที่ๆผมทำงาน Implement ระบบ Cache ด้านในไว้ เลยไม่มีปัญหามากครับ