How should I treat a wrong 404

I have a simple flow; I create an entry >> update >> read >> delete it. Entry is getting created just fine. I get the id from the header location field. When I go for update, I get error 404. I sent get request on the collected id and it does exist. I am not sure about what should I do in this situation. Should I add wait between requests so I don’t get 404 as the entry exists or think of it as the system is slow to respond and accept the error? Why way to go?

Hi @aakash.gupta,

Are you sure that if it waits for a while it fixes it self, because maybe your update call is just … not correct :wink:

This to me seems like a problem with your application. I would expect that if it was created I will be able to update it right after, or at least not get 404 but some other error that tells me I can’t update it.

So I would argue you should fix this in the application and count it as an error in k6 as well as in any other tests you have. Maybe though it is fine for you and you will need to work around it somehow, for example through retrying or just sleeping.

@aakash.gupta , Would you be able to provide a code example? It’ll help us understand better your scenario.

@jean-moldovan and for others, here is the code example:

import http from 'k6/http';
import {check, group, sleep} from 'k6';
import faker from 'https://cdnjs.cloudflare.com/ajax/libs/Faker/3.1.0/faker.min.js';
import {uuidv4} from 'https://jslib.k6.io/k6-utils/1.0.0/index.js';
import { Rate, Trend } from 'k6/metrics';
let createFailRate = new Rate('Create failed requests');
let updateFailRate = new Rate('Update failed requests');
let readFailRate = new Rate('Read failed requests');
let deleteFailRate = new Rate('Delete failed requests');

let createDurationTrend = new Trend('Create requests duration');
let updateDurationTrend = new Trend('Update requests duration');
let readDurationTrend = new Trend('Read requests duration');
let deleteDurationTrend = new Trend('Delete requests duration');

let createWaitingTrend = new Trend('Create requests waiting');
let updateWaitingTrend = new Trend('Update requests waiting');
let readWaitingTrend = new Trend('Read requests waiting');
let deleteWaitingTrend = new Trend('Delete requests waiting');

let createBlockedTrend = new Trend('Create requests blocked');
let updateBlockedTrend = new Trend('Update requests blocked');
let readBlockedTrend = new Trend('Read requests blocked');
let deleteBlockedTrend = new Trend('Delete requests blocked');

export const options = {
  setupTimeout: '10s',
  userAgent: 'MyK6UserAgentString/1.0',
  stages: [
    { duration: '3m', target: 10 },
    { duration: '5m', target: 20 },
    { duration: '5m', target: 20 },
    { duration: '5m', target: 10 },
    { duration: '2m', target: 5 },
  ],
  thresholds: {
  'Create failed requests': ['rate>.01'],
  'Update failed requests': ['rate>.01'],
  'Read failed requests': ['rate>.01'],
  'Delete failed requests': ['rate>.01'],
  'Create requests duration': ['p(99)<500'],
  'Update requests duration': ['p(99)<500'],
  'Read requests duration': ['p(99)<500'],
  'Delete requests duration': ['p(99)<500'],
  },
};

let catalogId;
let res;
let realmId;
let username;
let password;
let env;

if (__ENV.env == 'dev') {
  env = `alt1.${__ENV.env}`;
  realmId = '<id>';
  username = "<username>";
  password = "<password>";
}
else if (__ENV.env == 'staging') {
  env = `sa2.${__ENV.env}`;
  env = `alt1.${__ENV.env}`;
  realmId = '<id>';
  username = "<username>";
  password = "<password>";
}
else {
  this.assert.fail('Test failed. Wrong environment provided.');
}

export function setup() {
  const loginRequestBody =
    {
      'realm': `${realmId}`,
      'username': `${username}`,
      'password': `${password}`,
    };

  const loginRequestHeaders = {
    'Content-Type': 'application/json',
  };

  res = http.post(`https://${__ENV.env}/api/login`, loginRequestBody, loginRequestHeaders);

  const accessToken = res.json()['access_token'];
  return accessToken;
}

export default function(accessToken) {
  const suffix = Math.random().toString(36).substr(2, 5);
  const catalogName = 'K6 catalog create' + suffix;
  const updatedCatalogName = 'K6 catalog update' + suffix;
  let catalogEntryURL;

  group('Create', function create() {
    const requestBody = {
      'id': `${uuidv4()}`,
      'realm': `${realmId}`,
      'name': `${catalogName}`,
      'author': `${faker.name.findName()}`,
      'author-email': `${faker.internet.email()}`,
      'maintainer': `${faker.name.findName()}`,
      'maintainer-email': `${faker.internet.email()}`,
      'properties': {},
    };
    const headers = {
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        'Connection': 'keep-alive',
        'User-Agent': 'k6/0.27.0 (https://k6.io/)',
      },
    };
    res = http.post(`https://${env}/api/catalog/entries`, JSON.stringify(requestBody), headers);
    check(res, {
      'Create Catalog: is status 201': (r) => r.status === 201,
    });
    if (res.status != 201) {
          console.log(`create status: ${res.status}`);
        }
    createFailRate.add(res.status !== 201);
    createDurationTrend.add(res.timings.duration);
    createWaitingTrend.add(res.timings.waiting);
    createBlockedTrend.add(res.timings.blocked);
    if (res.status == 201) {
      catalogId = res.headers.Location.split('entries/')[1];
      console.log(`create catalog id is ${catalogId}`);
      group('Update', function update() {
      const updateRequestBody = {
        'id': `${catalogId}`,
        'realm': `${realmId}`,
        'name': `${updatedCatalogName}`,
        'author': `${faker.name.findName()}`,
        'author-email': `${faker.internet.email()}`,
        'maintainer': `${faker.name.findName()}`,
        'maintainer-email': `${faker.internet.email()}`,
        'properties': {},
      };
      const updateHeaders = {
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'Content-Type': 'application/json',
          'Accept': 'application/json',
          'Connection': 'keep-alive',
        },
      };
      catalogEntryURL = `https://${env}/api/catalog/entries/${catalogId}`;
      res = http.put(catalogEntryURL, JSON.stringify(updateRequestBody), updateHeaders);
      check(res, {
        'Update Catalog: is status 202': (r) => r.status === 202,
      });
        if (res.status != 202) {
          console.log(`Update status: ${res.status}`);
          console.log(`Update catalog id is ${catalogId}`);
          throw new Error(`Update status: ${res.status}`);
        }
      updateFailRate.add(res.status !== 202);
      updateDurationTrend.add(res.timings.duration);
      updateWaitingTrend.add(res.timings.waiting);
      updateBlockedTrend.add(res.timings.blocked);
      });
      sleep(.1);
      group('Read', function read() {
      const readHeaders = {
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'Accept': 'application/json, text/plain, */*',
        },
      };

      res = http.get(catalogEntryURL, readHeaders);
      check(res, {
        'Read Catalog: is status 200': (r) => r.status === 200
      });
        if (res.status != 200) {
          console.log(`Read status: ${res.status}`);
          console.log(`Read catalog id is ${catalogId}`);
        }
      readFailRate.add(res.status !== 200);
      readDurationTrend.add(res.timings.duration);
      readWaitingTrend.add(res.timings.waiting);
      readBlockedTrend.add(res.timings.blocked);
      });
      sleep(.1);
      group('Delete', function testing() {
      const deleteHeaders = {
        headers: {
          'Authorization': `Bearer ${accessToken}`,
        },
      };

      res = http.del(catalogEntryURL, null, deleteHeaders);
      check(res, {
        'Delete Status is 202': (r) => r.status === 202,
      });
        if (res.status != 202) {
          console.log(`delete status: ${res.status}`);
          console.log(`delete catalog id is ${catalogId}`);
        }
      deleteFailRate.add(res.status !== 202);
      deleteDurationTrend.add(res.timings.duration);
      deleteWaitingTrend.add(res.timings.waiting);
      deleteBlockedTrend.add(res.timings.blocked);
      });
      sleep(.1);
    }
  });
}

I agree. I am inclined towards counting 404 as error as I expect to be able to update/read/delete after create request is complete. But I should add random sleep time between requests to make the flow realistic.