@@ -404,6 +404,32 @@ type StandingChallengeRec = {
404404 standing : StandingChallenge [ ] ;
405405} ;
406406
407+ const sleep = ( ms : number ) => new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
408+
409+ async function sendCommandWithRetry ( command : any , maxRetries = 8 , initialDelay = 100 , maxDelay = 5000 ) {
410+ let retries = 0 ;
411+ while ( retries < maxRetries ) {
412+ try {
413+ // @ts -ignore
414+ return await ddbDocClient . send ( command ) ;
415+ } catch ( err : any ) {
416+ if ( [ 'ThrottlingException' , 'ProvisionedThroughputExceededException' , 'InternalServerError' , 'ServiceUnavailable' ] . includes ( err . name ) ) {
417+ retries ++ ;
418+ if ( retries >= maxRetries ) {
419+ console . error ( `Command failed after ${ maxRetries } retries.` ) ;
420+ throw err ;
421+ }
422+ const delay = Math . min ( initialDelay * Math . pow ( 2 , retries - 1 ) , maxDelay ) ;
423+ const jitter = delay * 0.1 * Math . random ( ) ;
424+ console . log ( `Retryable error (${ err . name } ) caught. Retrying in ${ Math . round ( delay + jitter ) } ms...` ) ;
425+ await sleep ( delay + jitter ) ;
426+ } else {
427+ throw err ;
428+ }
429+ }
430+ }
431+ }
432+
407433module . exports . query = async ( event : { queryStringParameters : any ; } ) => {
408434 console . log ( event ) ;
409435 const pars = event . queryStringParameters ;
@@ -1641,7 +1667,7 @@ async function updateUserGames(userId: string, gamesUpdate: undefined | number,
16411667 gameIDsChanged . length = 0 ;
16421668 if ( gamesUpdate === undefined ) {
16431669 // Update "old" users. This is a one-time update.
1644- return ddbDocClient . send ( new UpdateCommand ( {
1670+ return sendCommandWithRetry ( new UpdateCommand ( {
16451671 TableName : process . env . ABSTRACT_PLAY_TABLE ,
16461672 Key : { "pk" : "USER" , "sk" : userId } ,
16471673 ExpressionAttributeValues : { ":val" : 1 , ":gs" : games } ,
@@ -1650,7 +1676,7 @@ async function updateUserGames(userId: string, gamesUpdate: undefined | number,
16501676 } else {
16511677 console . log ( `updateUserGames: optimistically updating games for ${ userId } ` ) ;
16521678 try {
1653- await ddbDocClient . send ( new UpdateCommand ( {
1679+ await sendCommandWithRetry ( new UpdateCommand ( {
16541680 TableName : process . env . ABSTRACT_PLAY_TABLE ,
16551681 Key : { "pk" : "USER" , "sk" : userId } ,
16561682 ExpressionAttributeValues : { ":val" : gamesUpdate , ":inc" : 1 , ":gs" : games } ,
@@ -1688,21 +1714,25 @@ async function updateUserGames(userId: string, gamesUpdate: undefined | number,
16881714 }
16891715 try {
16901716 console . log ( `updateUserGames: Update ${ count } of games for user` , userId , newgames ) ;
1691- await ddbDocClient . send ( new UpdateCommand ( {
1717+ await sendCommandWithRetry ( new UpdateCommand ( {
16921718 TableName : process . env . ABSTRACT_PLAY_TABLE ,
16931719 Key : { "pk" : "USER" , "sk" : userId } ,
16941720 ExpressionAttributeValues : { ":val" : gamesUpdate , ":inc" : 1 , ":gs" : newgames } ,
16951721 ConditionExpression : "gamesUpdate = :val" ,
16961722 UpdateExpression : "set gamesUpdate = gamesUpdate + :inc, games = :gs"
16971723 } ) ) ;
16981724 return ;
1699- } catch ( err : any ) {
1700- count ++ ;
1725+ } catch ( innerErr : any ) {
1726+ if ( innerErr . name === 'ConditionalCheckFailedException' ) {
1727+ count ++ ;
1728+ } else {
1729+ throw innerErr ;
1730+ }
17011731 }
17021732 }
1703- new Error ( `updateUserGames: Unable to update games for user ${ userId } after 3 retries` ) ;
1733+ throw new Error ( `updateUserGames: Unable to update games for user ${ userId } after 3 retries` ) ;
17041734 } else {
1705- new Error ( err ) ;
1735+ throw err ;
17061736 }
17071737 }
17081738 }
@@ -2900,29 +2930,29 @@ function addToGameLists(type: string, game: Game, now: number, keepgame: boolean
29002930 const work : Promise < any > [ ] = [ ] ;
29012931 const sk = now + "#" + game . id ;
29022932 if ( type === "COMPLETEDGAMES" && keepgame ) {
2903- work . push ( ddbDocClient . send ( new PutCommand ( {
2933+ work . push ( sendCommandWithRetry ( new PutCommand ( {
29042934 TableName : process . env . ABSTRACT_PLAY_TABLE ,
29052935 Item : {
29062936 "pk" : type ,
29072937 "sk" : sk ,
29082938 ...game }
29092939 } ) ) ) ;
2910- work . push ( ddbDocClient . send ( new PutCommand ( {
2940+ work . push ( sendCommandWithRetry ( new PutCommand ( {
29112941 TableName : process . env . ABSTRACT_PLAY_TABLE ,
29122942 Item : {
29132943 "pk" : type + "#" + game . metaGame ,
29142944 "sk" : sk ,
29152945 ...game }
29162946 } ) ) ) ;
29172947 game . players . forEach ( ( player : { id : string ; } ) => {
2918- work . push ( ddbDocClient . send ( new PutCommand ( {
2948+ work . push ( sendCommandWithRetry ( new PutCommand ( {
29192949 TableName : process . env . ABSTRACT_PLAY_TABLE ,
29202950 Item : {
29212951 "pk" : type + "#" + player . id ,
29222952 "sk" : sk ,
29232953 ...game }
29242954 } ) ) ) ;
2925- work . push ( ddbDocClient . send ( new PutCommand ( {
2955+ work . push ( sendCommandWithRetry ( new PutCommand ( {
29262956 TableName : process . env . ABSTRACT_PLAY_TABLE ,
29272957 Item : {
29282958 "pk" : type + "#" + game . metaGame + "#" + player . id ,
@@ -2932,7 +2962,7 @@ function addToGameLists(type: string, game: Game, now: number, keepgame: boolean
29322962 } ) ;
29332963 }
29342964 if ( type === "CURRENTGAMES" ) {
2935- work . push ( ddbDocClient . send ( new UpdateCommand ( {
2965+ work . push ( sendCommandWithRetry ( new UpdateCommand ( {
29362966 TableName : process . env . ABSTRACT_PLAY_TABLE ,
29372967 Key : { "pk" : "METAGAMES" , "sk" : "COUNTS" } ,
29382968 ExpressionAttributeNames : { "#g" : game . metaGame } ,
@@ -2946,7 +2976,7 @@ function addToGameLists(type: string, game: Game, now: number, keepgame: boolean
29462976 update += ", #g.completedgames :n" ;
29472977 eavObj [ ":n" ] = 1
29482978 }
2949- work . push ( ddbDocClient . send ( new UpdateCommand ( {
2979+ work . push ( sendCommandWithRetry ( new UpdateCommand ( {
29502980 TableName : process . env . ABSTRACT_PLAY_TABLE ,
29512981 Key : { "pk" : "METAGAMES" , "sk" : "COUNTS" } ,
29522982 ExpressionAttributeNames : { "#g" : game . metaGame } ,
@@ -6542,7 +6572,7 @@ async function eventCreateGames(userid: string, pars: {eventid: string; pairs: P
65426572 }
65436573 }
65446574 // queue for update
6545- const addGame = ddbDocClient . send ( new PutCommand ( {
6575+ const addGame = sendCommandWithRetry ( new PutCommand ( {
65466576 TableName : process . env . ABSTRACT_PLAY_TABLE ,
65476577 Item : {
65486578 "pk" : "GAME" ,
@@ -6599,7 +6629,7 @@ async function eventCreateGames(userid: string, pars: {eventid: string; pairs: P
65996629 player2 : pair . p2 . id ,
66006630 } ;
66016631 list . push (
6602- ddbDocClient . send ( new PutCommand ( {
6632+ sendCommandWithRetry ( new PutCommand ( {
66036633 TableName : process . env . ABSTRACT_PLAY_TABLE ,
66046634 Item : eventGame ,
66056635 } ) )
0 commit comments