Building a Load Testing Framework using K6.io — Batch Requests Handling (Part 5)

In Part-04 of this article series, we added support for Assertions and Custom Metrics. In this post, we all see how to handle the use case of parallelism within threads using http.batch().

What is http.batch()?

it's another useful function from the 'h6/http' library which can batch multiple HTTP requests together to issue them in parallel over multiple TCP connections.

Batch request processing vs multiple API calls.Credit: https://api7.ai/blog/batch-request-processing-with-api-gateway

Let's dive into how to add batching to our k6 scripts.

We start by adding a new script file called "batch.js" as below:

You can just clone the test.js in batch.js and add following lines of code in "testSuite()" block by replacing pre-existing code.

// actual test code (run by VUs)
export default function testSuite() {

  describe('batch GET requests', () => {
      const responses = http.batch([
        ['GET', 'https://test.k6.io', null, { tags: { ctype: 'html' } }],
        ['GET', 'https://test.k6.io/style.css', null, { tags: { ctype: 'css' } }],
        ['GET', 'https://test.k6.io/images/logo.png', null, { tags: { ctype: 'images' } }],
      ]);

      check(responses[0], {
          'Home page status to be 200': (res) => res.status === 200,
      });

      check(responses[1], {
          'Style.css status to be 200': (res) => res.status === 200,
      });

      check(responses[2], {
          'Logo fetch status to be 200': (res) => res.status === 200,
      });
  })
}

Taken from Grafana K6 documentation

Explanation:

There are 3 essential parts of batching:

  • form an array of request objects
  • invoke the http.batch() with this array
  • iterate through responses to validate the outcome of requests


In the above code block we are essentially using the batch() to call multiple GET requests, by clubbing each request object in an array which is passed to http.batch(arrayOfRequests[]). K6 will execute these requests, which returns responses[] and then we can verify the individual response from responses[] one by one using checks. Pretty straight forward, right?

Run result of executing batch.js


Now that we know these key-points we can also extend this same behavior to our existing file upload code to make multiple file upload requests using batch().

Batching Multiple File Upload requests:

Let's begin by adding following code block to testSuite() which also follows the 3 steps approach we discussed above:

  1. we first form all the require request bodies for uploading the files from listOfFiles array
  2. we invoke uploadFilesUsingPOST(requestsArr) (which executes http.batch() fumction)
  3. Iterate through responses to validate the outcome and also create the fileUploadTime metris
// actual test code (run by VUs)
export default function testSuite() {

 ...

  describe('batch file upload', () => {

      //batch-document-upload
      let listOfFiles = ['Invoice-1.pdf', 'Invoice-2.pdf', 'Invoice-3.pdf']
      let allUploadJobRequestUrls = [];

      listOfFiles.forEach((currFile, index) => {
          //get a file
          let newFile = init.getAFile(currFile)

          //create endpoint URL for fileUpload
          const formedDocumentUploadJobUrl = `${baseUrl}/upload`
          console.log("Created upload URL:", formedDocumentUploadJobUrl)

          //create requestBody for fileUpload
          const currUploadJobRequestBody = {
              file: http.file(newFile.file, newFile.name),
              priority: true
          };
          // request object
          const currUploadJobRequest = {
              method: 'POST',
              url: formedDocumentUploadJobUrl,
              body: JSON.stringify(currUploadJobRequestBody),
              params: {
                  headers: {
                      'Content-Type': 'application/json'
                  },
              },
          }

          allUploadJobRequestUrls.push(currUploadJobRequest)
      })

      uploadFilesUsingPOST(allUploadJobRequestUrls)
  })
}

// function to make a batch request
function uploadFilesUsingPOST(requestsArr) {
    fileUploadStartTime = new Date().getTime() // file upload start time

    // run a batch of upload file requests
    const fileUploadResponses = http.batch(requestsArr)

    fileUploadResponses.forEach((currResponse) => {
        fileUploadEndTime = new Date().getTime() // file upload end time
        expect(currResponse.status,`Upload file response status ${currResponse.status}`).to.not.eq(200)
        console.log('currResponse filename:', currResponse.request.url)
    })

    fileUploadTime.add(fileUploadEndTime - fileUploadStartTime)

}

on executing the k6 run command we can see the following output:

  • Green box - corresponds to request formation
  • Red Box - corresponds to execution and verification
  • Blue Box - corresponds to metrics
Batch.js with file upload execution result
Note:

You would have noticed the difference between GET and Upload (POST) test code block. Both follow the 3-step batch process only thing is GET test code is very explicit, whereas the Upload (POST) test code is how it should be actually done in functional script.


Additionally, we can also control the maximum number of simultaneous/parallel connections for the same hostname that an http.batch() call in a VU can make. 

To set batch size, use the batch per host option.

export const options = {
  batchPerHost: 5,
};


so that's how we can leverage Batching in our scripts. until next time!!

GitHub repo:

https://github.com/far11ven/k6-LoadTestingFramework/tree/main/Part%2005?ref=kushalbhalaik.xyz