Error when updating user passed due to replication limit

I am encountering an error when attempting to change the user’s password. I have attached screenshots for your reference.

After the first submit:


Error on the console:
Screenshot from 2023-10-31 18-26-39

After the second submit
Screenshot from 2023-10-31 18-49-03

Console error:
Screenshot from 2023-10-31 18-58-42

Hi @KiharaSimon

Do you remember how this user was created?
A user needs two documents, in two different databases, and I’m suspecting you’re missing one of them.
Are you only getting this error with this specific user, or others as well? I don’t believe this has anything to do with the replication limit, so you can try making an update to any user to see if it succeeds or not.

Hello @diana ,

I resolved the issue by recreating the missing user document in the user database. This action fixed the problem. Thank you.

2 Likes

Hello Kihara,
I have encountered a similar error on my production and test server.
If you don’t mind, please explain to me how you resolved this issue.
Thanks,

It seems we might have multiple instances of CouchDB running on your server, potentially leading to a ‘first one to boot, wins’ scenario. Once you confirm this, you need a script to compare the content in the _users database with the medic database. If any inconsistencies are detected, the script should identify the number of affected documents and regenerate any missing ones in either the _users or medic database.

Below is a suggested script to accomplish this task and feel free improve it to fit your scope:

const fs = require('fs');
const csv = require('csv-parser');
const nano = require('nano');
const path = require('path');

// Configure CouchDB connection
const couchUrl = 'uri';
const usersDbName = '_users';
const medicDbName = 'medic';
const usersDB = nano(couchUrl).use(usersDbName);
const medicDB = nano(couchUrl).use(medicDbName);

// File path for usernames CSV and missing IDs CSV
const csvFilePath = path.join(__dirname, 'username-files', 'usernames.csv');
const missingIdsFilePath = path.join(__dirname, 'missing_ids.csv');

// Function to read usernames and passwords from CSV file
const getUsernameAndPasswordList = () => {
  return new Promise((resolve, reject) => {
    const userCredentials = [];
    fs.createReadStream(csvFilePath)
      .pipe(csv())
      .on('data', (row) => {
        if (row.username && row.password) {
          userCredentials.push({ username: row.username, password: row.password });
        }
      })
      .on('end', () => {
        resolve(userCredentials);
      })
      .on('error', (error) => {
        reject(error);
      });
  });
};

// Function to count existing and non-existing users in 'users' and 'medic' CouchDB databases
const countUsersExistence = async () => {
  try {
    const usernamesToCheck = await getUsernameAndPasswordList();
    let existingUsersCount = 0;
    let nonExistingUsersCount = 0;
    let existingMedicCount = 0;
    let nonExistingMedicCount = 0;
    let missingIds = [];

    // Check each username in 'users' and 'medic' CouchDB databases
    for (const user of usernamesToCheck) {
      try {
        await usersDB.get(user.username);
        existingUsersCount++;
      } catch (err) {
        if (err.statusCode === 404) {
          nonExistingUsersCount++;
          missingIds.push({ id: user.username, password: user.password }); // Collect missing IDs in _user Db with passwords
        } else {
          throw err;
        }
      }

      try {
        await medicDB.get(user.username);
        existingMedicCount++;
      } catch (err) {
        if (err.statusCode === 404) {
          nonExistingMedicCount++;
        } else {
          throw err;
        }
      }
    }

    console.log('Number of existing users in "users" CouchDB database:', existingUsersCount);
    console.log('Number of users that do not exist in "users" CouchDB database but exists:', nonExistingUsersCount);
    console.log('Number of existing users in "Medic" CouchDB database:', existingMedicCount);
    console.log('Number of users that do not exist in "Medic" CouchDB database:', nonExistingMedicCount);
    // console.log(missingIds);
    // Write missing IDs with passwords to a CSV file in the root folder
    const missingIdsCsvData = missingIds.map(item => ({ id: item.id, password: item.password }));
    const missingIdsCsvOutputPath = path.join(__dirname, 'missing_ids.csv');
    fs.writeFileSync(missingIdsCsvOutputPath, missingIdsCsvData.map(row => Object.values(row).join(',')).join('\n'));
    console.log('Missing IDs with passwords written to missing_ids.csv');

    // Fetch missing IDs from 'missing_ids.csv' to recreate user documents in '_users' database
    recreateUserDocs();
  } catch (error) {
    console.error('Error:', error);
  }
};

// Function to recreate user documents in '_users' database
const recreateUserDocs = async () => {
  try {
    // Read missing IDs with passwords from 'missing_ids.csv'
    const missingIdsList = fs
      .readFileSync(missingIdsFilePath, 'utf8')
      .split('\n')
      .map(line => {
        const [id, password] = line.trim().split(','); // Assuming format: id,password
        return { id, password };
      });

    // Recreate user documents in '_users' database
    for (const user of missingIdsList) {
      if (!user.id || !user.password) {
        console.error(`Invalid data format for user: ${JSON.stringify(user)}`);
        continue;
      }

      try {
        const medicDoc = await medicDB.get(user.id);

        const newUserDoc = {
          _id: user.id,
          name: medicDoc.name || '',
          type: 'user',
          roles: medicDoc.roles || '',
          facility_id: medicDoc.facility_id || '',
          password: user.password
          // Add other necessary fields from the 'medic' doc if needed
        };

        await usersDB.insert(newUserDoc);
        console.log(`Document for user ${user.id} created in '_users' database.`);
      } catch (err) {
        console.error(`Error recreating document for user ${user.id}:`, err);
      }
    }
  } catch (error) {
    console.error('Error:', error);
  }
};

// Execute the function to count users' existence and recreate user documents
countUsersExistence();
1 Like