UNPKG

@chevre/domain

Version:

Chevre Domain Library for Node.js

257 lines (234 loc) • 9.16 kB
// tslint:disable:no-console import * as moment from 'moment-timezone'; import * as mongoose from 'mongoose'; import * as redis from 'redis'; import { chevre } from '../../../../lib/index'; const today = moment() .tz('Asia/Tokyo') .format('YYYYMMDD'); const project = { id: String(process.env.PROJECT_ID) }; const providerId = 'sampleProviderId'; const eventId = `sampleEventId${today}:03`; const eventStartDate = new Date('2025-05-01T00:00:00Z'); const expires = moment(eventStartDate) .add(1, 'week') .toDate(); const seatSection = 'SampleSectionNameXXXXXXXXXXXXXXXXXXX'; // tslint:disable-next-line:no-magic-numbers prefer-array-literal const allSeatNumbers = [...Array(10000)].map((__, seatKey) => `SampleSeatNumber-${seatKey}`); const OPERATION_INTERVAL = 2000; // const MAXIMUM_CAPACITY = 10002; mongoose.Model.on('index', (...args) => { console.error('******** index event emitted. ********\n', args); }); async function sleep(waitTime: number): Promise<void> { return new Promise<void>((resolve) => { setTimeout( () => { resolve(); }, waitTime ); }); } const client = redis.createClient<redis.RedisDefaultModules, Record<string, never>, Record<string, never>>({ socket: { port: Number(<string>process.env.REDIS_PORT), host: <string>process.env.REDIS_HOST }, password: <string>process.env.REDIS_KEY }) .on('error', (err) => { // eslint-disable-next-line no-console console.error('createDefaultRedisClient: client onError:', err); // reject(err); }); client.connect(); mongoose.connect(<string>process.env.MONGOLAB_URI, { autoIndex: false }); const formatter = new Intl.NumberFormat('ja-JP'); // => "1,000" // tslint:disable-next-line:max-func-body-length async function lockSeatsForcibly(params: { maximumCapacity?: number; }) { const { maximumCapacity } = params; let startTime: [number, number] = process.hrtime(); let diff: [number, number] = process.hrtime(startTime); const stockHolderRepo = await chevre.repository.StockHolder.createInstance( client, mongoose.connection ); startTime = process.hrtime(); console.log('counting unavailableOffers...'); let countUnavailableOffersResult = await stockHolderRepo.countUnavailableOffers({ project: { id: project.id }, event: { id: eventId, startDate: eventStartDate, hasTicketedSeat: true } }); console.log('countUnavailableOffersResult:', countUnavailableOffersResult); console.log('diff:', [diff[0], formatter.format(diff[1])]); await sleep(OPERATION_INTERVAL); startTime = process.hrtime(); console.log('counting searching holders...'); const searchHoldersResult = await stockHolderRepo.searchHolders({ project: { id: project.id }, eventId, startDate: eventStartDate, hasTicketedSeat: true, offers: [ // tslint:disable-next-line:no-magic-numbers ...allSeatNumbers.slice(0, 100) .map((seatNumber) => ({ seatSection, seatNumber })) ] }); diff = process.hrtime(startTime); console.log('searchHoldersResult.length:', searchHoldersResult.length); console.log('diff:', [diff[0], formatter.format(diff[1])]); // select 2 seats // tslint:disable-next-line:insecure-random const seatNumber1 = allSeatNumbers[Math.floor(allSeatNumbers.length * Math.random())]; const seatNumber2 = allSeatNumbers.filter((seatNumber) => seatNumber !== seatNumber1)[ // tslint:disable-next-line:insecure-random Math.floor((allSeatNumbers.length - 1) * Math.random()) ]; const seatNumbers = [seatNumber1, seatNumber2]; console.log('seats selected.', seatNumbers); let locked = false; while (!locked) { await sleep(OPERATION_INTERVAL); startTime = process.hrtime(); const currentHolders: string[] = []; for (const seatNumber of seatNumbers) { const currentHolder = await stockHolderRepo.getHolder({ project: { id: project.id }, eventId, startDate: eventStartDate, hasTicketedSeat: true, offer: { seatSection, seatNumber } }); if (typeof currentHolder === 'string') { currentHolders.push(currentHolder); } } diff = process.hrtime(startTime); console.log('currentHolders:', currentHolders); console.log('diff:', [diff[0], formatter.format(diff[1])]); try { await sleep(OPERATION_INTERVAL); const newHolder = Date.now() .toString(); const bookingTime = new Date(); startTime = process.hrtime(); console.log('locking...', newHolder, seatSection, seatNumbers); if (typeof maximumCapacity === 'number') { try { await stockHolderRepo.lockIfNotLimitExceeded( { project: { id: project.id }, provider: { id: providerId }, eventId, startDate: eventStartDate, hasTicketedSeat: true, offers: seatNumbers.map((seatNumber) => ({ seatSection, seatNumber })), expires, holder: newHolder, bookingTime }, maximumCapacity ); } catch (error) { if (error.message === 'maximumAttendeeCapacity exceeded') { // ok } else { throw error; } } } else { await stockHolderRepo.lock({ project: { id: project.id }, provider: { id: providerId }, eventId, startDate: eventStartDate, hasTicketedSeat: true, offers: seatNumbers.map((seatNumber) => ({ seatSection, seatNumber })), expires, holder: newHolder, bookingTime }); } diff = process.hrtime(startTime); console.log('locked.', newHolder); console.log('diff:', [diff[0], formatter.format(diff[1])]); locked = true; } catch (error) { if (error instanceof chevre.factory.errors.AlreadyInUse) { console.log('lockResult:', error.name, error.message); } else { console.error(error); } } await sleep(OPERATION_INTERVAL); for (const currentHolder of currentHolders) { for (const seatNumber of seatNumbers) { startTime = process.hrtime(); console.log('unlocking...', currentHolder, seatSection, seatNumber); const unlockResult = await stockHolderRepo.unlock({ project: { id: project.id }, eventId, startDate: eventStartDate, hasTicketedSeat: true, offer: { seatSection, seatNumber }, holder: currentHolder }); diff = process.hrtime(startTime); console.log('unlockResult:', unlockResult); console.log('diff:', [diff[0], formatter.format(diff[1])]); } } } await sleep(OPERATION_INTERVAL); countUnavailableOffersResult = await stockHolderRepo.countUnavailableOffers({ project: { id: project.id }, event: { id: eventId, startDate: eventStartDate, hasTicketedSeat: true } }); console.log('countUnavailableOffersResult:', countUnavailableOffersResult); } async function main() { // tslint:disable-next-line:no-magic-numbers const numLock = (typeof process.argv[2] === 'string') ? Number(process.argv[2]) : 1; // tslint:disable-next-line:no-magic-numbers const maximumCapacity = (typeof process.argv[3] === 'string') ? Number(process.argv[3]) : undefined; const LOCK_INTERVAL = 300; let i = 0; let lockedCount = 0; let timeout: NodeJS.Timeout; const processStartDate = new Date(); function onSeatsLocked() { lockedCount += 1; console.log(lockedCount, 'lockSeatsForcibly executed!'); console.log('processed.', processStartDate, '~', new Date()); } timeout = setInterval( () => { if (i >= numLock) { clearInterval(timeout); return; } i += 1; lockSeatsForcibly({ maximumCapacity }) .then(onSeatsLocked) .catch(console.error); }, LOCK_INTERVAL ); } main() .then() .catch(console.error);