Syntax highlighter header

Monday, 20 July 2020

Setting Compression Level in GZIPOutputStream

Most of the time people want to compress files which they are generating to save space on disk and bandwidth in transmission. Apart from saving space compression can actually speed up the application because of low disk usage because of small file size. For this the compression and decompression need to done in memory and not after writing whole uncompressed contents to disk.

There are two formats which can be used if you are generating files from Java. One is GZIPOutputStream which is used for generating GZIP files, other is ZipOutputStream which is used for generating ZIP files. There is one basic difference between GZIP and ZIP file. GZIP file can contain only one file inside it and name of the file contained inside it is optional and while ZIP file is an archive of multiple files and name of the files contained in a ZIP file is mandatory while creating a ZIP file. Because of presence of multiple files inside a ZIP file. ZIP file cannot be passed to a filter which will decompress a ZIP file on the fly from an input stream because filter can't select one file out of multiple files which may be present in the ZIP file.

For seamless processing of compressed file while reading GZIP format is most suitable one. But unfortunately Java API for GZIPOutputStream lacks one method which can be used to controlling compression level to achieve BEST_SPEED or BEST_COMPRESSION as per your need. This facility is available in ZipOutputStream. Sometime people just use ZipOutput stream by setting compression level to BEST_SPEED to gain performance  when they actually need GZIPOutputStream for compressing their data. It create problem for the reader because now he need to handle a archive which can potentially contain multiple file rather than a compressed file. In memory filters can't be used for decompression because of possibility of multiple files in ZIP file. Therefore there are no libraries which can provide in memory filter for reading ZIP file contents as a stream of data.

Fortunately you can set compression level in GZIPOutputStream also by creating sub class of GZIPOutputStream and exposing setLevel(int level) method in your subclass.  We did it in our code and achieved even slightly better results than using ZipOutputStream with BEST_SPEED compression level. Following is comparison when compressing a 5.4 GB file:


Zip compression with BEST_SPEED        48078219 bytes     80 seconds
GZip compression with BEST_SPEED     48078113 bytes     78 seconds

Here is the code for MyGZIPOutputStream class:

import java.util.zip.*;
import java.io.*;

public class MyGZIPOutputStream extends GZIPOutputStream
{
    /**
     * Creates a new output stream with the specified buffer size.
     * @param out the output stream
     * @param size the output buffer size
     * @exception IOException If an I/O error has occurred.
     * @exception IllegalArgumentException if size is <= 0
     */
    public MyGZIPOutputStream(OutputStream out, int size) throws IOException {
        super(out, size);
    }

    /**
     * Creates a new output stream with a default buffer size.
     * @param out the output stream
     * @exception IOException If an I/O error has occurred.
     */
    public MyGZIPOutputStream(OutputStream out) throws IOException {
        this(out, 512);
    }

    /**
     * Sets the compression level for subsequent entries which are DEFLATED.
     * The default setting is DEFAULT_COMPRESSION.
     * @param level the compression level (0-9)
     * @exception IllegalArgumentException if the compression level is invalid
     */
    public void setLevel(int level) {
        def.setLevel(level);
    }
}

Sample file for using it BEST_SPEED compression in GZIPOutputStream:

import java.io.BufferedWriter;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.zip.*;

public class GZipCompression {

    public static void main(String[] args) throws IOException {
        compressInputFile("a.txt", "a.txt.gz");
    }

    public static void compressInputFile(String inputFileName,
            String outputFileName) throws IOException {
        FileOutputStream fos = new FileOutputStream(new File(outputFileName));
        MyGZIPOutputStream gzos = null;
        byte[] buffer = new byte[1024];
        gzos = new MyGZIPOutputStream(fos);
        gzos.setLevel(Deflater.BEST_SPEED);
        long startTime = System.currentTimeMillis();              

        FileInputStream fis = new FileInputStream(inputFileName);

        int length;
        while ((length = fis.read(buffer)) > 0) {
            gzos.write(buffer, 0, length);
        }
        fis.close();
        gzos.close();

        long endTime = System.currentTimeMillis();
        System.out.println("Time taken to gzip "+ (endTime-startTime) + " miliseconds.");
    }
}

Wednesday, 8 July 2020

Mounting EFS volume on EC2 machine

In my previous post I explained you how to mount EFS volume inside a fargate task container. EFS serves as persistent storage for ephemeral  container. Sometime you may want to see the data containers are storing in EFS. Most of the time containers are special purpose containers like MongoDB which does not provide any interface to browse the file system.

The easy way to look at EFS is to mount it on an EC2 instance. EFS volume can be mounted on multiple machines therefore you can mount it on EC2 machine and inside the container at the same time. You need to look at DNS name for EFS volume.

This DNS name is used while mounting EFS volume as a NFS drive on EC2 volume.

Create a directory for mounting EFS volume. For example I am creating /efs3


sudo mkdir /efs3

Now you can mount the EFS volume using following command. Please replace DNS name of your EFS volume:

sudo mount -t nfs -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport fs-9ce1684d.efs.ap-south-1.amazonaws.com:/ 
 /efs3

You can follow the following link for more information https://docs.aws.amazon.com/efs/latest/ug/mounting-fs-mount-cmd-dns-name.html

Saturday, 27 June 2020

Mounting EFS volume in fargate task in AWS

In this post I am going to explain process of mounting an EFS disk in fargate service in AWS.
First step is to define volumes in task definitions. I am going to use an file browser container in this exercise which allow you to browse root file system of container. This container should be used only for experimentation and should be terminated as soon as you are done with your experiment.

I am creating a task definition fargate-filebrowser1 of fargate type.


Click on "Add Volume" link at the bottom of task definition page and fill in details of EFS volume.

After adding EFS volume to task definitions add container to task definition. You can add multiple containers to one task definitions but we will add only one. After clicking "Add Container" button provide container name and image name:

The Image name is jasvantsingh/myfilebrowser:latest it is based on filebrowser/filebrowser with just one change that it exposes root file system rather than one directory.

Scroll down to bottom of container options and provide mount point for EFS volume inside the container.

The EFS volume will be mounted at /vol2 as per this configuration. Any file created inside /vol2 directory will be persisted across task restart or deletion of service and recreation of service. EFS is a kind of NFS mount which reside on some other persistent storage outside the container.


Click on "Create" button under service tab of your cluster.

Provide service details. Please note that you need to select PLATFORM version 1.4.0 and not LATEST. LATEST version does not work maybe it is not mapping to 1.4.0


On the next screen provide networking details. Please make sure that "Auto-assign Public IP" is ENABLED. I will not be using any load balancer so the task need to have a public IP address to be accessed from outside.


On Auto-scaling screen keep auto scaling as disabled.


On review screen click create service button. The service will be created. Click on "View Service" button. It will take you to service details page. Wait for some for task to be started and listed in Task tab.


Click on the task ID. It will take you to task details page. Please note the public IP address of the task.


Paste the public IP Address in browser and access the file browser. User username as admin and password as admin for login in the file browser. You can see "vol2" directory there. This is persistent directory. You can place any file inside this directory and it will be persisted.


Please make sure to terminate the service and task because anybody can hack into this file browser and put malicious content there without your knowledge. This file browser is only for experimentation.

Please comment if something is not working. I will reply to your comment.

EFS does not work with fargate service in AWS

On April 8, 2020 Amazon announced availability of EFS for fargate services. I spent one week trying to mount EFS volume in my fargate service with no success. I was using platform version as LATEST. The service startup always failed with error:
Service creation failed: One or more of the requested capabilities are not supported.
I was trying to follow the tutorial at:
https://aws.amazon.com/blogs/aws/amazon-ecs-supports-efs/
After struggling for one week I noticed the following line in tutorial.
It’s also essential here to make sure that I set the platform version to 1.4.0
When I tried 1.4.0 platform version it worked.  Due to some reason 1.4.0 and LATEST are not same. EFS works in 1.4.0 but not in LATEST platform version.

Tuesday, 28 April 2020

Number of ways to insert two pairs of parentheses into a string of N characters

I came across an interesting codding problem at https://www.geeksforgeeks.org/number-of-ways-to-insert-two-pairs-of-parentheses-into-a-string-of-n-characters/

Problem:
Given a string str of length N, the task is to find the number of ways to insert only 2 pairs of parentheses into the given string such that the resultant string is still valid.
Example:
Input: str = “ab”Output: 6((a))b, ((a)b), ((ab)), (a)(b), (a(b)), a((b))which are a total of 6 ways.
Solution:
Approach: it can be observed that for the lengths of the string 1, 2, 3, …, N a series will be formed as 1, 6, 20, 50, 105, 196, 336, 540, … whose Nth term is (N + 1)2 * ((N + 1)2 – 1) / 12. 
The solution is good but the page does not provide derivation of the formula which is base of the solution.  I tried to derive the formula on my own and this is what I came up with:

  1. There are N+1 places where parenthesis can be placed. One in the beginning, N-1 in middle and one at end. So there are   ((N + 1)4 )/4 ways to place the parenthesis. We can place multiple parenthesis at same location, one opening parenthesis can be replaced with another opening parenthesis and closing parenthesis can be replaced with other closing parenthesis therefore we divided by 4.
  2. Some of the combinations counted in previous step are invalid so we need to subtract them. 
  3. All four parenthesis at same location is not a valid case there are (N+1)/4  such combinations so we need to subtract them.
  4. Three parenthesis being at one location and one being at one location is also not a valid combination. There are (N+1)*N/4 such combinations so we need to subtract them.
  5. After the above considerations We come up with (N + 1)2 * ((N + 1)2 – 1) / 4 ways.
  6. Now if you think closely out of following 6 possible combinations of parenthesis only first two are valid. You ignore string character in between parenthesis.
    1. (())
    2. ()()
    3. )()(
    4. )(()
    5. ))((
    6. ())(
  7. So we need to divide by 3 to get valid ways of putting parenthesis. So we get the final formula as (N + 1)2 * ((N + 1)2 – 1) / 12.