├── .gitignore ├── app.js ├── cluster.js ├── data └── requests ├── database.js ├── decomposing_services ├── data-reservations │ └── reservations ├── data-shows │ └── shows ├── reservations.js ├── shows.js └── ticket-system.js ├── fork.js ├── horizontal_partition ├── data-a-m │ └── dogs ├── data-n-z │ └── dogs ├── data │ └── dogs ├── db.js └── index.js ├── orchestration ├── api.js ├── data-reservations │ └── reservations ├── data-shows │ └── shows ├── reservations.js └── show.js ├── package.json ├── pm2.js └── zero-downtime.js /.gitignore: -------------------------------------------------------------------------------- 1 | log/ 2 | node_modules/ 3 | package-lock.json -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const http = require('http') 2 | const port = parseInt(process.argv[2] || '3000') 3 | 4 | const options = [ 5 | "Go for it!", 6 | "Maybe sleep on it", 7 | "Do some more research", 8 | "I don't know", 9 | "I wouldn't" 10 | ] 11 | 12 | const server = http.createServer((req, res) => { 13 | const randomIndex = Math.floor(Math.random() * options.length) 14 | const payload = JSON.stringify({ 15 | port, 16 | processID: process.pid, 17 | advise: options[randomIndex] 18 | }) 19 | 20 | res.writeHead(200, { 'Content-Type': 'application/json'}) 21 | res.end(payload) 22 | }) 23 | 24 | server.listen(port) 25 | console.log(`advise service running on port ${port}`) 26 | -------------------------------------------------------------------------------- /cluster.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const cluster = require('cluster'); 3 | const numCPUs = require('os').cpus().length 4 | 5 | if(cluster.isMaster){ 6 | console.log("this is the master process:", process.pid); 7 | for(let i=0; i { 12 | const message = `Worker: ${process.pid}`; 13 | console.log(message); 14 | res.end(message) 15 | }).listen(3000) 16 | } 17 | -------------------------------------------------------------------------------- /data/requests: -------------------------------------------------------------------------------- 1 | 10 -------------------------------------------------------------------------------- /database.js: -------------------------------------------------------------------------------- 1 | const http = require('http') 2 | const { LocalStorage } = require('node-localstorage'); 3 | 4 | const db = new LocalStorage('./data') 5 | 6 | let requests = 0 7 | 8 | const server = http.createServer((req, res) => { 9 | if (req.url === '/') { 10 | requests++ 11 | console.log(`${process.pid}: ${requests}`) 12 | res.end(JSON.stringify(requests)) 13 | } 14 | }) 15 | 16 | server.listen(3000) 17 | console.log(`counting requests`) 18 | -------------------------------------------------------------------------------- /decomposing_services/data-reservations/reservations: -------------------------------------------------------------------------------- 1 | { 2 | "5b805a00297ae6047030f2e0": [ 3 | { 4 | "name": "WDJ", 5 | "guests": 3 6 | }, 7 | { 8 | "name": "Eve", 9 | "guests": 7 10 | } 11 | ], 12 | "5b805a790cee1505a52bc75e": [ 13 | { 14 | "name": "WDJ", 15 | "guests": 75 16 | }, 17 | { 18 | "name": "Eve", 19 | "guests": 125 20 | }, 21 | { 22 | "name": "Scooby", 23 | "guests": 50 24 | }, 25 | { 26 | "name": "Jungle", 27 | "guests": 25 28 | }, 29 | { 30 | "name": "Daryle", 31 | "guests": 25 32 | }, 33 | { 34 | "name": "Cheryle", 35 | "guests": 100 36 | }, 37 | { 38 | "name": "Meryle", 39 | "guests": 10 40 | }, 41 | { 42 | "name": "Jones", 43 | "guests": 15 44 | }, 45 | { 46 | "name": "Jinkins", 47 | "guests": 50 48 | }, 49 | { 50 | "name": "Johanas", 51 | "guests": 20 52 | }, 53 | { 54 | "name": "Jerry", 55 | "guests": 5 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /decomposing_services/data-shows/shows: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "_id": "5b805a00297ae6047030f2e0", 4 | "name": "yodeling Concert", 5 | "houseSize": 100, 6 | "reserved": 10 7 | }, 8 | { 9 | "_id": "5b805a570cee1505a52bc75d", 10 | "name": "Shakespeare Play", 11 | "houseSize": 50, 12 | "reserved": 10 13 | }, 14 | { 15 | "_id": "5b805a790cee1505a52bc75e", 16 | "name": "Food Poison Rally", 17 | "houseSize": 500, 18 | "reserved": 500 19 | } 20 | ] -------------------------------------------------------------------------------- /decomposing_services/reservations.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const bodyParser = require('body-parser') 3 | const cors = require('cors') 4 | const { LocalStorage } = require('node-localstorage') 5 | 6 | const localStorage = new LocalStorage('./data-reservations') 7 | 8 | const loadReservations = () => JSON.parse(localStorage.getItem('reservations') || '{}') 9 | const saveReservations = reservations => localStorage.setItem('reservations', JSON.stringify(reservations, null, 2)) 10 | 11 | const app = express() 12 | .use(cors()) 13 | .use(bodyParser.json()) 14 | .use(bodyParser.urlencoded()) 15 | 16 | 17 | .delete('/cancel', (req, res) => { 18 | const reservations = loadReservations() 19 | const { showID, name } = req.body 20 | const reservation = reservations[showID].find(reservation => reservation.name === name) 21 | reservations[showID] = reservations[showID].filter(reservation => reservation.name !== name) 22 | saveReservations(reservations) 23 | res.json({ canceled: true, showID, ...reservation }) 24 | }) 25 | .post('/reserveTickets', (req, res) => { 26 | const reservations = loadReservations() 27 | const shows = loadShows() 28 | let count 29 | if (!req.body.count) { 30 | res.status(500) 31 | return res.json({ error: `A ticket count is required to reserve tickets.`}) 32 | } 33 | if (!req.body.name) { 34 | res.status(500) 35 | return res.json({ error: `A name is required to reserve tickets.`}) 36 | } 37 | count = parseInt(req.body.count) 38 | show = shows.find(s => s._id === req.body.showID) 39 | if (!show) { 40 | res.status(500) 41 | return res.json({ error: `Cannot find show with id: ${req.body.showID}`}) 42 | } 43 | const remainingSeats = show.houseSize - show.reserved 44 | if (remainingSeats < count) { 45 | res.status(500) 46 | return res.json({ error: `cannot reserve ${count} seats. Only ${remainingSeats} remaining.`}) 47 | } 48 | 49 | var list = reservations[req.body.showID] 50 | var reservation = { name: req.body.name, guests: req.body.count } 51 | if (!list) { 52 | reservations[req.body.showID] = [] 53 | } 54 | reservations[req.body.showID].push(reservation) 55 | show.reserved += count 56 | saveReservations(reservations) 57 | saveShows(shows) 58 | res.json({ success: true, showID: req.body.showID, ...reservation}) 59 | }) 60 | .get('/reservations/:showID', (req, res) => { 61 | const reservations = loadReservations() 62 | res.json(reservations[req.params.showID] || []) 63 | }) 64 | .get('/', (req, res) => { 65 | const reservations = loadReservations() 66 | res.json(reservations) 67 | console.log('reservations returned') 68 | }) 69 | 70 | app.listen(3002, () => console.log(`reservation service running on port 3002`)) 71 | -------------------------------------------------------------------------------- /decomposing_services/shows.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const bodyParser = require('body-parser') 3 | const cors = require('cors') 4 | const { LocalStorage } = require('node-localstorage') 5 | 6 | const localStorage = new LocalStorage('./data-shows') 7 | 8 | const loadShows = () => JSON.parse(localStorage.getItem('shows') || '[]') 9 | const saveShows = shows => localStorage.setItem('shows', JSON.stringify(shows, null, 2)) 10 | 11 | const app = express() 12 | .use(cors()) 13 | .use(bodyParser.json()) 14 | .use(bodyParser.urlencoded()) 15 | .put('/release-seats', (req, res) => { 16 | let show, count, shows = loadShows() 17 | if (!req.body.showID || !req.body.count) { 18 | res.status(500) 19 | return res.json({ error: 'A showID and count are required to release seats'}) 20 | } 21 | count = parseInt(req.body.count) 22 | show = shows.find(s => s._id === req.body.showID) 23 | if (!show) { 24 | res.status(500) 25 | return res.json({ error: `Cannot find show with id: ${req.body.showID}`}) 26 | } 27 | show.reserved -= count 28 | if (show.reserved < 0) { 29 | show.reserved = 0 30 | } 31 | saveShows(shows) 32 | res.json(show) 33 | }) 34 | .put('/hold-seats', (req, res) => { 35 | let show, count, shows = loadShows() 36 | if (!req.body.showID || !req.body.count) { 37 | res.status(500) 38 | return res.json({ error: 'A showID and count are required to hold seats'}) 39 | } 40 | count = parseInt(req.body.count) 41 | show = shows.find(s => s._id === req.body.showID) 42 | if (!show) { 43 | res.status(500) 44 | return res.json({ error: `Cannot find show with id: ${req.body.showID}`}) 45 | } 46 | const remainingSeats = show.houseSize - show.reserved 47 | if (remainingSeats < count) { 48 | res.status(500) 49 | return res.json({ error: `cannot reserve ${count} seats. Only ${remainingSeats} remaining.`}) 50 | } 51 | show.reserved += count 52 | saveShows(shows) 53 | res.json(show) 54 | }) 55 | .get('/:id', (req, res) => { 56 | const shows = loadShows() 57 | const show = shows.find(show => show._id === req.params.id) 58 | res.json(show) 59 | console.log(`delivered show ${show.name}`) 60 | }) 61 | .get('/', (req, res) => { 62 | const shows = loadShows() 63 | res.json(shows) 64 | console.log('shows returned') 65 | }) 66 | 67 | app.listen(3001, () => console.log(`show service running on port 3001`)) 68 | -------------------------------------------------------------------------------- /decomposing_services/ticket-system.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const bodyParser = require('body-parser') 3 | const cors = require('cors') 4 | const { LocalStorage } = require('node-localstorage') 5 | 6 | const localStorage = new LocalStorage('./data') 7 | 8 | const loadShows = () => JSON.parse(localStorage.getItem('shows') || '[]') 9 | const saveShows = shows => localStorage.setItem('shows', JSON.stringify(shows, null, 2)) 10 | 11 | const loadReservations = () => JSON.parse(localStorage.getItem('reservations') || '{}') 12 | const saveReservations = reservations => localStorage.setItem('reservations', JSON.stringify(reservations, null, 2)) 13 | 14 | const app = express() 15 | .use(cors()) 16 | .use(bodyParser.json()) 17 | .use(bodyParser.urlencoded()) 18 | .put('/release-seats', (req, res) => { 19 | let show, count, shows = loadShows() 20 | if (!req.body.showID || !req.body.count) { 21 | res.status(500) 22 | return res.json({ error: 'A showID and count are required to release seats'}) 23 | } 24 | count = parseInt(req.body.count) 25 | show = shows.find(s => s._id === req.body.showID) 26 | if (!show) { 27 | res.status(500) 28 | return res.json({ error: `Cannot find show with id: ${req.body.showID}`}) 29 | } 30 | show.reserved -= count 31 | if (show.reserved < 0) { 32 | show.reserved = 0 33 | } 34 | saveShows(shows) 35 | res.json(show) 36 | }) 37 | .put('/hold-seats', (req, res) => { 38 | let show, count, shows = loadShows() 39 | if (!req.body.showID || !req.body.count) { 40 | res.status(500) 41 | return res.json({ error: 'A showID and count are required to hold seats'}) 42 | } 43 | count = parseInt(req.body.count) 44 | show = shows.find(s => s._id === req.body.showID) 45 | if (!show) { 46 | res.status(500) 47 | return res.json({ error: `Cannot find show with id: ${req.body.showID}`}) 48 | } 49 | const remainingSeats = show.houseSize - show.reserved 50 | if (remainingSeats < count) { 51 | res.status(500) 52 | return res.json({ error: `cannot reserve ${count} seats. Only ${remainingSeats} remaining.`}) 53 | } 54 | show.reserved += count 55 | saveShows(shows) 56 | res.json(show) 57 | }) 58 | .delete('/cancel', (req, res) => { 59 | const reservations = loadReservations() 60 | const { showID, name } = req.body 61 | const reservation = reservations[showID].find(reservation => reservation.name === name) 62 | reservations[showID] = reservations[showID].filter(reservation => reservation.name !== name) 63 | saveReservations(reservations) 64 | res.json({ canceled: true, showID, ...reservation }) 65 | }) 66 | .post('/reserveTickets', (req, res) => { 67 | const reservations = loadReservations() 68 | const shows = loadShows() 69 | let count 70 | if (!req.body.count) { 71 | res.status(500) 72 | return res.json({ error: `A ticket count is required to reserve tickets.`}) 73 | } 74 | if (!req.body.name) { 75 | res.status(500) 76 | return res.json({ error: `A name is required to reserve tickets.`}) 77 | } 78 | count = parseInt(req.body.count) 79 | show = shows.find(s => s._id === req.body.showID) 80 | if (!show) { 81 | res.status(500) 82 | return res.json({ error: `Cannot find show with id: ${req.body.showID}`}) 83 | } 84 | const remainingSeats = show.houseSize - show.reserved 85 | if (remainingSeats < count) { 86 | res.status(500) 87 | return res.json({ error: `cannot reserve ${count} seats. Only ${remainingSeats} remaining.`}) 88 | } 89 | 90 | var list = reservations[req.body.showID] 91 | var reservation = { name: req.body.name, guests: req.body.count } 92 | if (!list) { 93 | reservations[req.body.showID] = [] 94 | } 95 | reservations[req.body.showID].push(reservation) 96 | show.reserved += count 97 | saveReservations(reservations) 98 | saveShows(shows) 99 | res.json({ success: true, showID: req.body.showID, ...reservation}) 100 | }) 101 | .get('/reservations/:showID', (req, res) => { 102 | const reservations = loadReservations() 103 | res.json(reservations[req.params.showID] || []) 104 | }) 105 | .get('/show/:id', (req, res) => { 106 | const shows = loadShows() 107 | const show = shows.find(show => show._id === req.params.id) 108 | res.json(show) 109 | console.log(`delivered show ${show.name}`) 110 | }) 111 | .get('/', (req, res) => { 112 | const shows = loadShows() 113 | const reservations = loadReservations() 114 | res.json({shows, reservations}) 115 | console.log('shows and reservations returned') 116 | }) 117 | 118 | app.listen(3000, () => console.log(`entire ticket system running on port 3000`)) 119 | -------------------------------------------------------------------------------- /fork.js: -------------------------------------------------------------------------------- 1 | const {fork} = require('child_process') 2 | 3 | const processes = [ 4 | fork('./app', ['3001']), 5 | fork('./app', ['3002']), 6 | fork('./app', ['3003']) 7 | ] 8 | 9 | console.log(`forked ${processes.length} processes`); 10 | -------------------------------------------------------------------------------- /horizontal_partition/data-a-m/dogs: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "biscuit", 4 | "color": "orange" 5 | }, 6 | { 7 | "name": "jungle", 8 | "color": "black" 9 | }, 10 | { 11 | "name": "fancy feast", 12 | "color": "white" 13 | }, 14 | { 15 | "name": "bread", 16 | "color": "orange" 17 | } 18 | ] -------------------------------------------------------------------------------- /horizontal_partition/data-n-z/dogs: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "smokey", 4 | "color": "grey" 5 | }, 6 | { 7 | "name": "peep", 8 | "color": "orange" 9 | } 10 | ] -------------------------------------------------------------------------------- /horizontal_partition/data/dogs: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "biscuit", 4 | "color": "orange" 5 | }, 6 | { 7 | "name": "jungle", 8 | "color": "black" 9 | }, 10 | { 11 | "name": "smokey", 12 | "color": "grey" 13 | }, 14 | { 15 | "name": "fancy feast", 16 | "color": "white" 17 | }, 18 | { 19 | "name": "peep", 20 | "color": "orange" 21 | }, 22 | { 23 | "name": "bread", 24 | "color": "orange" 25 | } 26 | ] -------------------------------------------------------------------------------- /horizontal_partition/db.js: -------------------------------------------------------------------------------- 1 | const { LocalStorage } = require('node-localstorage') 2 | 3 | const dbA = new LocalStorage('data-a-m') 4 | const dbB = new LocalStorage('data-n-z') 5 | 6 | const whichDB = name => name.match(/^[A-M]|^[a-m]/) ? dbA : dbB 7 | 8 | const loadDogs = db => JSON.parse(db.getItem("dogs") || '[]') 9 | 10 | const hasDog = name => loadDogs(whichDB(name)) 11 | .map(dog => dog.name) 12 | .includes(name) 13 | 14 | module.exports = { 15 | 16 | addDog(newDog) { 17 | if (!hasDog(newDog.name)) { 18 | let db = whichDB(newDog.name) 19 | let dogs = loadDogs(db) 20 | dogs.push(newDog) 21 | db.setItem("dogs", JSON.stringify(dogs, null, 2)) 22 | } 23 | }, 24 | 25 | findDogByName(name) { 26 | let db = whichDB(name) 27 | let dogs = loadDogs(db) 28 | return dogs.find(dog => dog.name === name) 29 | }, 30 | 31 | findDogsByColor(color) { 32 | return [ 33 | ...loadDogs(dbA).filter(dog => dog.color === color), 34 | ...loadDogs(dbB).filter(dog => dog.color === color) 35 | ] 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /horizontal_partition/index.js: -------------------------------------------------------------------------------- 1 | const db = require('./db') 2 | 3 | db.addDog({ name: 'biscuit', color: 'orange' }) 4 | db.addDog({ name: 'jungle', color: 'black' }) 5 | db.addDog({ name: 'smokey', color: 'grey' }) 6 | db.addDog({ name: 'fancy feast', color: 'white' }) 7 | db.addDog({ name: 'peep', color: 'orange' }) 8 | db.addDog({ name: 'bread', color: 'orange' }) 9 | 10 | var biscuit = db.findDogByName('biscuit') 11 | var orange_dogs = db.findDogsByColor('orange') 12 | 13 | console.log('biscuit: ', biscuit) 14 | console.log('orange dogs: ', orange_dogs) 15 | -------------------------------------------------------------------------------- /orchestration/api.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const bodyParser = require('body-parser') 3 | const cors = require('cors') 4 | const fetch = require('node-fetch') 5 | 6 | const getAllShows = () => 7 | fetch('http://localhost:3001') 8 | .then(res => res.json()) 9 | 10 | const getShow = id => 11 | fetch(`http://localhost:3001/${id}`) 12 | .then(res => res.json()) 13 | 14 | const holdSeats = (showID, count) => 15 | fetch(`http://localhost:3001/hold-seats`, { 16 | method: 'PUT', 17 | headers: { 18 | 'Content-Type': 'application/json' 19 | }, 20 | body: JSON.stringify({ count, showID }) 21 | }).then(res => res.json()) 22 | 23 | const makeReservation = (name, count, showID) => 24 | fetch(`http://localhost:3002`, { 25 | method: 'POST', 26 | headers: { 27 | 'Content-Type': 'application/json' 28 | }, 29 | body: JSON.stringify({ name, count, showID }) 30 | }).then(res => res.json()) 31 | 32 | const app = express() 33 | .use(cors()) 34 | .use(bodyParser.json()) 35 | .use(bodyParser.urlencoded()) 36 | .post('/reserve', async (req, res) => { 37 | 38 | let count, show 39 | 40 | if (!req.body.count) { 41 | res.status(500) 42 | return res.json({ error: `A ticket count is required to reserve tickets.`}) 43 | } 44 | 45 | if (!req.body.name) { 46 | res.status(500) 47 | return res.json({ error: `A name is required to reserve tickets.`}) 48 | } 49 | 50 | // Parse the Count 51 | count = parseInt(req.body.count) 52 | 53 | // Lookup the Show 54 | show = await getShow(req.body.showID) 55 | 56 | if (!show) { 57 | res.status(500) 58 | return res.json({ error: `Cannot find show with id: ${req.body.showID}`}) 59 | } 60 | 61 | const remainingSeats = show.houseSize - show.reserved 62 | 63 | if (remainingSeats < count) { 64 | res.status(500) 65 | return res.json({ error: `cannot reserve ${count} seats. Only ${remainingSeats} remaining.`}) 66 | } 67 | 68 | // Hold Seats with Show Service 69 | console.log(`holding ${count} seats for ${req.body.name}`) 70 | await holdSeats(req.body.showID, count) 71 | 72 | // Make Reservation with Reservation Service 73 | console.log(`making the reservation for ${req.body.count}`); 74 | const reservation = await makeReservation(req.body.name, count, req.body.showID); 75 | 76 | res.json({ success: true, showID: req.body.showID, ...reservation}) 77 | 78 | }) 79 | .get('/', async (req, res) => { 80 | // Return a List of Shows Only 81 | console.log("requesting shows from show service"); 82 | var shows = await getAllShows(); 83 | res.json(shows); 84 | }) 85 | 86 | app.listen(3000, () => console.log(`Show Ticket API running for all clients`)) 87 | -------------------------------------------------------------------------------- /orchestration/data-reservations/reservations: -------------------------------------------------------------------------------- 1 | { 2 | "5b805a00297ae6047030f2e0": [ 3 | { 4 | "name": "WDJ", 5 | "guests": 3 6 | }, 7 | { 8 | "name": "Eve", 9 | "guests": 7 10 | } 11 | ], 12 | "5b805a790cee1505a52bc75e": [ 13 | { 14 | "name": "WDJ", 15 | "guests": 75 16 | }, 17 | { 18 | "name": "Eve", 19 | "guests": 125 20 | }, 21 | { 22 | "name": "Scooby", 23 | "guests": 50 24 | }, 25 | { 26 | "name": "Jungle", 27 | "guests": 25 28 | }, 29 | { 30 | "name": "Daryle", 31 | "guests": 25 32 | }, 33 | { 34 | "name": "Cheryle", 35 | "guests": 100 36 | }, 37 | { 38 | "name": "Meryle", 39 | "guests": 10 40 | }, 41 | { 42 | "name": "Jones", 43 | "guests": 15 44 | }, 45 | { 46 | "name": "Jinkins", 47 | "guests": 50 48 | }, 49 | { 50 | "name": "Johanas", 51 | "guests": 20 52 | }, 53 | { 54 | "name": "Jerry", 55 | "guests": 5 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /orchestration/data-shows/shows: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "_id": "5b805a00297ae6047030f2e0", 4 | "name": "yodeling Concert", 5 | "houseSize": 100, 6 | "reserved": 18 7 | }, 8 | { 9 | "_id": "5b805a570cee1505a52bc75d", 10 | "name": "Shakespeare Play", 11 | "houseSize": 50, 12 | "reserved": 30 13 | }, 14 | { 15 | "_id": "5b805a790cee1505a52bc75e", 16 | "name": "Food Poison Rally", 17 | "houseSize": 500, 18 | "reserved": 500 19 | } 20 | ] -------------------------------------------------------------------------------- /orchestration/reservations.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const bodyParser = require('body-parser') 3 | const cors = require('cors') 4 | const { LocalStorage } = require('node-localstorage') 5 | 6 | const localStorage = new LocalStorage('./data-reservations') 7 | 8 | const loadReservations = () => JSON.parse(localStorage.getItem('reservations') || '{}') 9 | const saveReservations = reservations => localStorage.setItem('reservations', JSON.stringify(reservations, null, 2)) 10 | 11 | const app = express() 12 | .use(cors()) 13 | .use(bodyParser.json()) 14 | .use(bodyParser.urlencoded()) 15 | .delete('/cancel', (req, res) => { 16 | const reservations = loadReservations() 17 | const { showID, name } = req.body 18 | const reservation = reservations[showID].find(reservation => reservation.name === name) 19 | reservations[showID] = reservations[showID].filter(reservation => reservation.name !== name) 20 | saveReservations(reservations) 21 | res.json({ canceled: true, showID, ...reservation }) 22 | }) 23 | .post('/reserveTickets', (req, res) => { 24 | const reservations = loadReservations() 25 | const shows = loadShows() 26 | let count 27 | if (!req.body.count) { 28 | res.status(500) 29 | return res.json({ error: `A ticket count is required to reserve tickets.`}) 30 | } 31 | if (!req.body.name) { 32 | res.status(500) 33 | return res.json({ error: `A name is required to reserve tickets.`}) 34 | } 35 | count = parseInt(req.body.count) 36 | show = shows.find(s => s._id === req.body.showID) 37 | if (!show) { 38 | res.status(500) 39 | return res.json({ error: `Cannot find show with id: ${req.body.showID}`}) 40 | } 41 | const remainingSeats = show.houseSize - show.reserved 42 | if (remainingSeats < count) { 43 | res.status(500) 44 | return res.json({ error: `cannot reserve ${count} seats. Only ${remainingSeats} remaining.`}) 45 | } 46 | 47 | var list = reservations[req.body.showID] 48 | var reservation = { name: req.body.name, guests: req.body.count } 49 | if (!list) { 50 | reservations[req.body.showID] = [] 51 | } 52 | reservations[req.body.showID].push(reservation) 53 | show.reserved += count 54 | saveReservations(reservations) 55 | saveShows(shows) 56 | res.json({ success: true, showID: req.body.showID, ...reservation}) 57 | }) 58 | .get('/reservations/:showID', (req, res) => { 59 | const reservations = loadReservations() 60 | res.json(reservations[req.params.showID] || []) 61 | }) 62 | .get('/', (req, res) => { 63 | const reservations = loadReservations() 64 | res.json(reservations) 65 | console.log('reservations returned') 66 | }) 67 | 68 | app.listen(3002, () => console.log(`reservation service running on port 3002`)) 69 | -------------------------------------------------------------------------------- /orchestration/show.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const bodyParser = require('body-parser') 3 | const cors = require('cors') 4 | const { LocalStorage } = require('node-localstorage') 5 | 6 | const localStorage = new LocalStorage('./data-shows') 7 | 8 | const loadShows = () => JSON.parse(localStorage.getItem('shows') || '[]') 9 | const saveShows = shows => localStorage.setItem('shows', JSON.stringify(shows, null, 2)) 10 | 11 | const app = express() 12 | .use(cors()) 13 | .use(bodyParser.json()) 14 | .use(bodyParser.urlencoded()) 15 | .put('/release-seats', (req, res) => { 16 | let show, count, shows = loadShows() 17 | if (!req.body.showID || !req.body.count) { 18 | res.status(500) 19 | return res.json({ error: 'A showID and count are required to release seats'}) 20 | } 21 | count = parseInt(req.body.count) 22 | show = shows.find(s => s._id === req.body.showID) 23 | if (!show) { 24 | res.status(500) 25 | return res.json({ error: `Cannot find show with id: ${req.body.showID}`}) 26 | } 27 | show.reserved -= count 28 | if (show.reserved < 0) { 29 | show.reserved = 0 30 | } 31 | saveShows(shows) 32 | res.json(show) 33 | }) 34 | .put('/hold-seats', (req, res) => { 35 | let show, count, shows = loadShows() 36 | if (!req.body.showID || !req.body.count) { 37 | res.status(500) 38 | return res.json({ error: 'A showID and count are required to hold seats'}) 39 | } 40 | count = parseInt(req.body.count) 41 | show = shows.find(s => s._id === req.body.showID) 42 | if (!show) { 43 | res.status(500) 44 | return res.json({ error: `Cannot find show with id: ${req.body.showID}`}) 45 | } 46 | const remainingSeats = show.houseSize - show.reserved 47 | if (remainingSeats < count) { 48 | res.status(500) 49 | return res.json({ error: `cannot reserve ${count} seats. Only ${remainingSeats} remaining.`}) 50 | } 51 | show.reserved += count 52 | saveShows(shows) 53 | res.json(show) 54 | }) 55 | .get('/:id', (req, res) => { 56 | const shows = loadShows() 57 | const show = shows.find(show => show._id === req.params.id) 58 | res.json(show) 59 | console.log(`delivered show ${show.name}`) 60 | }) 61 | .get('/', (req, res) => { 62 | const shows = loadShows() 63 | res.json(shows) 64 | console.log('shows returned') 65 | }) 66 | 67 | app.listen(3001, () => console.log(`show service running on port 3001`)) 68 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Scaling-Applications", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/webdevjourneyWDJ/Scaling-Node-JS-Application.git" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/webdevjourneyWDJ/Scaling-Node-JS-Application/issues" 18 | }, 19 | "homepage": "https://github.com/webdevjourneyWDJ/Scaling-Node-JS-Application#readme", 20 | "dependencies": { 21 | "cors": "^2.8.5", 22 | "express": "^4.17.1", 23 | "node-fetch": "^2.6.0", 24 | "node-localstorage": "^2.1.5" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pm2.js: -------------------------------------------------------------------------------- 1 | const http = require('http') 2 | const options = [ 3 | "yes", 4 | "no" 5 | ] 6 | 7 | const server = http.createServer((req, res) => { 8 | const randomIndex = Math.floor(Math.random() * options.length) 9 | const advice = options[randomIndex] 10 | const payload = JSON.stringify({ 11 | processID: process.pid, 12 | advice 13 | }) 14 | console.log(`advice from ${process.pid}: ${advice}`) 15 | res.writeHead(200, { 'Content-Type': 'application/json'}) 16 | res.end(payload) 17 | }) 18 | 19 | server.listen(3000) 20 | console.log(`advice service running`) 21 | -------------------------------------------------------------------------------- /zero-downtime.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const cluster = require('cluster'); 3 | const numCPUs = require('os').cpus().length 4 | 5 | if(cluster.isMaster){ 6 | console.log("this is the master process:", process.pid); 7 | for(let i=0; i { 12 | console.log(`worker process ${worker.process.pid} had died.`); 13 | console.log(`only ${Object.keys(cluster.workers).length} remainging`) 14 | console.log(`starting new worker`); 15 | cluster.fork(); 16 | }) 17 | }else { 18 | console.log(`started a worker at ${process.pid}`); 19 | http.createServer((req, res) => { 20 | res.end(`process: ${process.pid}`) 21 | if(req.url === '/kill'){ 22 | process.exit() 23 | }else if (req.url === '/') { 24 | console.log(`serving from ${process.pid}`); 25 | } 26 | }).listen(3000) 27 | } 28 | --------------------------------------------------------------------------------