Skip to content

Commit 75b7133

Browse files
committed
log replication failures for users without medic user-settings docs
Signed-off-by: Diana Barsan <barsan@medic.org>
1 parent 51c3cb6 commit 75b7133

5 files changed

Lines changed: 254 additions & 89 deletions

File tree

api/src/middleware/authorization.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
const auth = require('../auth');
22
const serverUtils = require('../server-utils');
3+
const logger = require('@medic/logger');
4+
const replicationFailureLog = require('../services/replication/replication-failure-log');
35

46
const FIREWALL_ERROR = {
57
code: 403,
@@ -94,5 +96,23 @@ module.exports = {
9496
}
9597

9698
return getUserSettings(req).then(next);
99+
},
100+
101+
captureReplicationFailures: (req, res, next) => {
102+
if (auth.isOnlineOnly(req.userCtx)) {
103+
return next();
104+
}
105+
106+
const start = Date.now();
107+
res.on('close', () => {
108+
if (!res.writableFinished || res.statusCode >= 400) {
109+
const duration = Date.now() - start;
110+
const statusCode = res.writableFinished ? res.statusCode : 0;
111+
replicationFailureLog
112+
.capture(req.userCtx, req.id, statusCode, duration)
113+
.catch(err => logger.error('Failed to persist replication failure log: %o', err));
114+
}
115+
});
116+
next();
97117
}
98118
};

api/src/routing.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,12 +689,14 @@ app.all(
689689
app.get(
690690
'/api/v1/initial-replication/get-ids',
691691
authorization.handleAuthErrors,
692+
authorization.captureReplicationFailures,
692693
authorization.onlineUserPassThrough,
693694
replication.getDocIds,
694695
);
695696
app.get(
696697
'/api/v1/replication/get-ids',
697698
authorization.handleAuthErrors,
699+
authorization.captureReplicationFailures,
698700
authorization.onlineUserPassThrough,
699701
replication.getDocIds,
700702
);

api/src/services/replication/authorization.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,8 @@ const getAuthorizationContext = async (userCtx) => {
377377
authCtx.subjectsDepth[UNASSIGNED_KEY] = 0;
378378
}
379379

380+
authCtx.userCtx.subjectsCount = authCtx.subjectIds.length;
381+
380382
return authCtx;
381383
};
382384

Lines changed: 35 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,51 @@
1-
const moment = require('moment');
21
const db = require('../../db');
3-
const logger = require('@medic/logger');
4-
5-
const LOG_TYPE = 'replication-fail-';
6-
// The count and month difference between old and new log.
7-
// To determine when the old log will updated with the new one.
8-
const LOG_COUNT_DIFF = 100;
9-
const LOG_MONTH_DIFF = 1;
10-
11-
const persistLog = async (info) => {
12-
if (!info?.user) {
13-
throw new Error('Error when persisting log: Log Information missing.');
14-
}
15-
16-
const logDocId = LOG_TYPE + info.user;
17-
18-
19-
20-
return db.medicLogs
21-
.get(logDocId)
22-
.catch(error => {
23-
if (error.status === 404) {
24-
return { _id: logDocId };
25-
}
26-
throw error;
27-
})
28-
.then(doc => {
29-
if (!isLogDifferent(doc, info)) {
30-
return;
31-
}
32-
33-
const logDoc = Object.assign(doc, info);
34-
return db.medicLogs.put(logDoc);
35-
});
36-
};
37-
38-
const isLogDifferent = (oldLog, newLog) => {
39-
if (!oldLog.date && !oldLog.count) {
40-
return true;
41-
}
2+
const moment = require('moment');
423

43-
const countDiff = Math.abs(oldLog.count - newLog.count);
4+
const LOG_TYPE = 'replication-fail';
5+
const MAX_FAILURES = 50;
446

45-
const oldPrePurge = oldLog.all_docs_count || 0;
46-
const newPrePurge = newLog.all_docs_count || 0;
7+
const captureFailure = async (userCtx, requestId, statusCode, duration) => {
8+
const log = await getLog(userCtx.name);
479

48-
const prePurgeDiff = Math.abs(oldPrePurge - newPrePurge);
10+
const failure = {
11+
timestamp: moment().valueOf(),
12+
status_code: statusCode,
13+
duration: duration,
14+
request_id: requestId,
15+
subjects_count: userCtx.subjectsCount,
16+
roles: userCtx.roles,
17+
};
4918

50-
if (countDiff > LOG_COUNT_DIFF || prePurgeDiff > LOG_COUNT_DIFF) {
51-
return true;
19+
log.failures.push(failure);
20+
log.total_failures++;
21+
if (log.failures.length > MAX_FAILURES) {
22+
log.failures = log.failures.slice(-MAX_FAILURES);
5223
}
5324

54-
const oldLogDate = moment(oldLog.date);
55-
const newLogDate = moment(newLog.date);
56-
const monthDiff = newLogDate.diff(oldLogDate, 'months', true);
57-
58-
return monthDiff > LOG_MONTH_DIFF;
59-
};
60-
61-
const getLogsByType = (docPrefix) => {
62-
const options = {
63-
startkey: docPrefix,
64-
endkey: docPrefix + '\ufff0',
65-
include_docs: true
66-
};
67-
68-
return db.medicLogs
69-
.allDocs(options)
70-
.then((result) => result.rows.map(row => row.doc));
25+
return db.medicLogs.put(log);
7126
};
27+
const getDocId = (userName) => `${LOG_TYPE}-${moment().format('YYYY-MM')}-${userName}`;
7228

73-
const getReplicationLimitLog = (userName) => {
74-
const get = !userName ? getLogsByType(LOG_TYPE) : db.medicLogs.get(LOG_TYPE + userName);
29+
const getLog = async (userName) => {
30+
const docId = getDocId(userName);
7531

76-
return get
77-
.then(logs => {
32+
try {
33+
return await db.medicLogs.get(docId);
34+
} catch (err) {
35+
if (err.status === 404) {
7836
return {
79-
limit: DOC_IDS_WARN_LIMIT,
80-
users: logs
37+
_id: docId,
38+
type: LOG_TYPE,
39+
user: userName,
40+
timestamp: moment().valueOf(),
41+
total_failures: 0,
42+
failures: [],
8143
};
82-
});
83-
};
84-
85-
const logReplicationLimit = (userName, count, prePurgeCount) => {
86-
const info = {
87-
user: userName,
88-
date: moment().valueOf(),
89-
count,
90-
all_docs_count: prePurgeCount
91-
};
92-
93-
return persistLog(info)
94-
.catch(error => {
95-
logger.error('Error on Log Replication Limit %o', error);
96-
});
44+
}
45+
throw err;
46+
}
9747
};
9848

9949
module.exports = {
100-
put: logReplicationLimit,
101-
get: getReplicationLimitLog,
102-
_isLogDifferent: isLogDifferent,
103-
LOG_TYPE,
104-
DOC_IDS_WARN_LIMIT,
50+
capture: captureFailure,
10551
};

0 commit comments

Comments
 (0)