Skip to content

Commit 9ef409c

Browse files
authoredAug 19, 2023
Completed memcached server with add, set, get, replace support and tests (#20)
* Completed memcached server with add, set, get, replace support and tests * added support for no reply, updated tests * Fixed tests * fixed tests * updated tests * segregated tests * added error logging * updated datetime usage and readme * updated exptime and added at to store ms since epoch * added debug logging * added debug logging
1 parent cb6f891 commit 9ef409c

9 files changed

+755
-4
lines changed
 

‎README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ npm test
3737
To run tests for specific challenge, use the following command:
3838

3939
```bash
40-
# npm test tests/<challenge-number>
41-
npm test tests/2
42-
npm test tests/3
40+
# npm test tests/<challenge-number>/
41+
npm test tests/2/
42+
npm test tests/3/
4343
```

‎jest.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ module.exports = {
1111
coverageDirectory: './coverage',
1212
collectCoverage: true,
1313
detectOpenHandles: true,
14-
silent: false,
14+
silent: false
1515
};

‎package-lock.json

+55
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"dotenv": "^16.3.1",
2121
"express": "^4.18.2",
2222
"helmet": "^7.0.0",
23+
"memcached": "^2.2.2",
2324
"redis": "^4.6.7",
2425
"ts-node": "^10.9.1",
2526
"winston": "^3.10.0"
@@ -30,6 +31,7 @@
3031
"@babel/preset-typescript": "^7.22.5",
3132
"@types/express": "^4.17.17",
3233
"@types/jest": "^29.5.3",
34+
"@types/memcached": "^2.2.7",
3335
"@types/node": "^20.4.2",
3436
"@types/supertest": "^2.0.12",
3537
"@typescript-eslint/eslint-plugin": "^6.0.0",

‎src/17/command.ts

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
type SupportCommands = 'set' | 'get' | 'add' | 'replace';
2+
3+
export interface IMemCommand {
4+
/**
5+
* The name of the support command.
6+
*
7+
* @type {SupportCommands}
8+
*/
9+
name: SupportCommands;
10+
11+
/**
12+
* The key corresponding to the command.
13+
*
14+
* @type {string}
15+
*/
16+
key: string;
17+
18+
/**
19+
* Flags for the command.
20+
* This is used in set, add and replace command.
21+
*
22+
* @type {?number}
23+
*/
24+
flags?: number;
25+
26+
/**
27+
* The expiry time in seconds for the data.
28+
* This is used in set, add and replace command.
29+
*
30+
* @type {?number}
31+
*/
32+
expTime?: number;
33+
34+
/**
35+
* Number of bytes for the data.
36+
* This is used in set, add and replace command.
37+
* @date 8/18/2023 - 9:14:01 PM
38+
*
39+
* @type {?number}
40+
*/
41+
byteCount?: number;
42+
43+
/**
44+
* If true then the server will not reply to the command.
45+
*
46+
* @type {?boolean}
47+
*/
48+
noReply?: boolean;
49+
}
50+
51+
export class MemCommand implements IMemCommand {
52+
name;
53+
key;
54+
flags?;
55+
expTime?;
56+
byteCount?;
57+
noReply?;
58+
59+
constructor(
60+
commandName: SupportCommands,
61+
key: string,
62+
flags?: number,
63+
expTime?: number,
64+
byteCount?: number,
65+
noReply: boolean = false
66+
) {
67+
this.name = commandName;
68+
this.key = key;
69+
this.flags = flags;
70+
this.expTime = expTime;
71+
this.byteCount = byteCount;
72+
this.noReply = noReply;
73+
}
74+
}
75+
76+
/**
77+
* Function used to parse a valid command.
78+
* The commands follow the following format:
79+
*
80+
* `<command name> <key> <flags> <expTime> <byte count> [noreply]`
81+
*
82+
* The trailing CRLF is already removed while reading the data from the socket.
83+
*
84+
* @export
85+
* @param {string} input
86+
* @returns {IMemCommand}
87+
*/
88+
export function parseMemCommand(input: string): IMemCommand {
89+
// Split the command params with space
90+
const params = input.split(' ');
91+
92+
const name = params[0] as SupportCommands;
93+
94+
const key = params[1];
95+
96+
let flags;
97+
if (params[2]) {
98+
flags = parseInt(params[2]);
99+
}
100+
101+
let expTime;
102+
if (params[3] && (name === 'set' || name === 'add' || name === 'replace')) {
103+
try {
104+
expTime = parseInt(params[3]);
105+
} catch (e) {
106+
expTime = undefined;
107+
}
108+
}
109+
110+
let byteCount;
111+
if (params[4] && (name === 'set' || name === 'add' || name === 'replace')) {
112+
byteCount = parseInt(params[4]);
113+
}
114+
115+
const noreply = params[params.length - 1] === 'noreply';
116+
117+
return new MemCommand(name, key, flags, expTime, byteCount, noreply);
118+
}

‎src/17/memcached.index.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import MemCachedServer from './memcached';
2+
3+
let port = 11211;
4+
const host = '127.0.0.1';
5+
6+
for (let i = 2; i < process.argv.length; i++) {
7+
const arg = process.argv[i];
8+
if (arg === '-p') {
9+
try {
10+
port = parseInt(process.argv[i + 1]);
11+
} catch (e) {
12+
console.error('Invalid port provided');
13+
process.exit(1);
14+
}
15+
}
16+
}
17+
18+
async function main() {
19+
const server = new MemCachedServer(port, host);
20+
server.startServer();
21+
}
22+
23+
main();

0 commit comments

Comments
 (0)