Click to Contract/Expend
minikube tunnel
# ./ticketing/client
docker build -t pcsmomo/client .
docker push pcsmomo/client # skaffold pull the image from here
# ./ticketing
skaffold dev
kubectl create secret generic jwt-secret --from-literal=JWT_KEY=asdfadd 127.0.0.1 ticketing.dev to /etc/hosts
# temporarily port forwarding NATS services
k get pods
# NATS client
k port-forward nats-depl-588b8b6b8-2s9nt 4222:4222
# NATS mornitoring service (not necessary. For debugging)
k port-forward nats-depl-588b8b6b8-2s9nt 8222:8222# NATS publisher and listener in separated tabs
npm run publish
npm run listen # x2 or x3, for more listener(=consumer)- Duplicate the 'tickets' service
- Install dependencies
- Build an image out of the order service
- Create a Kubernetes deployment file
- Set up file sync options in the skaffold.yaml file
- Set up routing rules in the ingress service
# ticketing/orders
npm install
docker build -t pcsmomo/orders .
docker push pcsmomo/orders# ticketing
skaffold dev # had to run a few timesWe will check ticketId if it is a mongodbID which makes subtle coupling
If you don't like this coupling, this .custom() line can be just deleted
body('ticketId')
.not()
.isEmpty()
.custom((input: string) => mongoose.Types.ObjectId.isValid(input))
.withMessage('TicketId must be provided');We need to somehow associate Tickets and Orders together
"Ticket Document", "Order Document"
Two primary ways to do this with MongoDB/mongoose
- Option #1: Embedding
{ ..., "ticket" {} }- Querying is a bit challenging
- There are tickets not ordered, but order service woulnd't know about them
- Option #2: Mongoose Ref/Population Feature
# udemy/microservices-node-react/dwktickets-npm/commmon
npm run pub
# + @dwktickets/common@1.0.7// if it is production, we should not use "!"". It is just for test
expect(updatedOrder!.status).toEqual(OrderStatus.Cancelled);# udemy/microservices-node-react/dwktickets-npm/commmon
npm run pub
# + @dwktickets/common@1.0.9
# made a mistake for 1.0.8
# auth, tickets, orders
npm update @dwktickets/commonPostman manual test
- Signup: https://ticketing.dev/api/users/signup
- Check user: https://ticketing.dev/api/users/currentuser
- Create a new ticket: POST https://ticketing.dev/api/tickets
- Update the ticket: PUT https://ticketing.dev/api/tickets
# skaffold dev
# [tickets] Event published to subject ticket:created
# [orders] Message received: ticket:created / orders-service
# [orders] Message received: ticket:updated / orders-service
# [tickets] Event published to subject ticket:updated(Optional) Set up "concurrency-test" script to simulate
- creating a ticket with price 5
- modify the price to 10
- modify the price to 15
- run them 200 times
- Cleanup database
- tickets-mongo
# see tickets in k get pods k exec -it <tickets-mongo-pod> sh mongosh test> show dbs # admin 40.00 KiB # config 108.00 KiB # local 40.00 KiB # tickets 72.00 KiB test> use tickets tickets> show collections # tickets tickets> db.tickets.find({}) # ... tickets> db.tickets.remove({})
- orders-mongo
# see tickets in k get pods k exec -it <orders-mongo-pod> sh mongosh test> show dbs # admin 40.00 KiB # config 108.00 KiB # local 40.00 KiB # orders 80.00 KiB test> use orders orders> show collections # orders # tickets orders> db.tickets.find({}) # ... orders> db.tickets.remove({})
- tickets-mongo
- Make 600 (3 * 200) requests
# ticketing/playground/concurrency-test npm start - Check the database
# probably the order version of mongodb uses .length() orders> db.tickets.find({ price: 10 }).count() # 0 tickets> db.tickets.find({ price: 10 }).count() # It should be 0, but when there's concurrent issue. # there'd be some tickets with price 10, (wasn't updated to 15)
npm mongoose-update-if-current
# ticketing/tickets
npm install mongoose-update-if-currentmongoose is managing version with __v flag, but we will use version
Optimistic Concurrency Issue : OCC
// ticketing/tickets/src/models/ticket.ts
ticketSchema.set('versionKey', 'version');Who should we increment or include the 'version' number of a record with an event?
-> Increment/include the 'version' number whenever the primary service responsible for a record
emits an event to describe a create/update/detroy to a record
# udemy/microservices-node-react/dwktickets-npm/commmon
npm run pub
# + @dwktickets/common@1.0.10
# tickets, orders, (auth)
# update package.json first, skaffold will install the version defined in package.json
# "@dwktickets/common": "^1.0.10",
npm update @dwktickets/common# ticketing/orders
npm install mongoose-update-if-currentGo to [Simulate 1,200 (3 * 400) requests]
# ticketing/playground/concurrency-test
npm starttickets> db.tickets.find({ price: 15 }).count()
# 400
orders> db.tickets.find({ price: 15 }).count()
# 400
what does mongoose-update-if-current do
- Updates the version number on records before they are saved
# ticketing/orders/src/events/listeners/ticket-updated-listener.ts const { title, price, version } = data; ticket.set({ title, price, version }); await ticket.save(); - Customizes the find-and-update operation (save) to look for the correct version
# ticketing/orders/src/events/models/ticket.ts // ticketSchema.plugin(updateIfCurrentPlugin); // Must use a stand function to bind, not an arrow function ticketSchema.pre('save', function (done) { this.$where = { version: this.get('version') - 1, }; done(); });
# udemy/microservices-node-react/dwktickets-npm/commmon
npm run pub
# + @dwktickets/common@1.0.11
# tickets, orders, (auth)
# update package.json first, skaffold will install the version defined in package.json
# "@dwktickets/common": "^1.0.11",
npm update @dwktickets/commonsee inside of jest.fn() mock function for debugging
// ticketing/tickets/src/events/listeners/__test__/order-created-listener.test.ts
// @ts-ignore
console.log(natsWrapper.client.publish.mock.calls);
// without @ts-ignore
(natsWrapper.client.publish as jest.Mock).mock.calls[0][1];