1

I have a lambda function that uses xstate to perform certain tasks sequentially and one of the steps is to save data to dynamo db. But my lambda ends execution as soon as the below line is being executed.

const response = await new DynamoDB.DocumentClient().put(params).promise();

my code :

import {Handler} from "aws-lambda";
import {interpret} from 'xstate';
import { Machine} from "xstate";
import {PutItemInput} from "aws-sdk/clients/dynamodb";
import {DynamoDB} from "aws-sdk";

export const recordProcessor: Handler = async (event) => {
  console.log('records size----->', event.Records.length);
  for (const record of event.Records) {
      const body = JSON.parse(record.body);
      console.log('body ----->', body);

    interpret(Machine({id:'test',
      context:body,
      initial: 'start',
      states:{
        start: {
          invoke: {
            src: context => initiate(context),
            onDone: {
              target: 'success'
            }
          }
        },
        success: {
          type: 'final'
        }
      }
    })).onTransition(state => {
      if (state.changed) {
        console.log('state ----> ', state.value);
      }
    }).onDone(() => console.log('done--->')).start();
  }


  async function initiate(context: any) {
    console.log('DbDynamoImpl ::: insert ::: start :::');
    let params: PutItemInput = {
      TableName: 'test',
      Item: context
    };
    try {
      const response = await new DynamoDB.DocumentClient().put(params).promise();
      console.log('DbDynamoImpl ::: insert ::: response :::', response);
      return true;
    } catch (e) {
      console.log("DynamoDb insert error", e);
      return false;
    }
  }

};
Vipul Fadte
  • 63
  • 1
  • 8
  • hard to say anything without looking at the complete code. – Ashish Modi Mar 07 '20 at 14:26
  • added test code as I cannot share my whole code. – Vipul Fadte Mar 07 '20 at 14:53
  • I had to change implementation from xstate to chain of responsibility design pattern due to project timeline and haven't really got time to check by Greg and Iko. But will check as soon as get time and update... Others also free to upvote if it works. – Vipul Fadte Mar 10 '21 at 06:44

3 Answers3

0

you are resolving Promise before hitting return statement. Can you try to resolve Promise with values that you need to return? Then it will go to onDone block with those values. Also having try and catch block over here is redundant as service in xstate will automatically handle that for you.

Just return the promise in service invoke statement, because it is needs to be a function that return a Promise

function initiate(context: any) {
  console.log('DbDynamoImpl ::: insert ::: start :::');
  let params: PutItemInput = {
    TableName: 'test',
    Item: context,
  };
  return new DynamoDB.DocumentClient().put(params).promise();
}

Then in your onDone block you can console.log('DbDynamoImpl ::: insert ::: response :::', response);

onDone: {
  target: 'success',
  actions: (ctx, e) => console.log('DbDynamoImpl ::: insert ::: response :::', e),
}
Greg Konush
  • 572
  • 1
  • 5
  • 9
0

Wrapping the machine interpret, and interactions into a promise allows you to wait for the state machine to hit a final state.

Something roughly like this


export const recordProcessor: Handler = async (event) => {
  console.log('records size----->', event.Records.length);
  for (const record of event.Records) {
      const body = JSON.parse(record.body);
      console.log('body ----->', body);
      await setupMachine(...);
...
}

function setupMachine(...){
  return new Promise((resolve, reject) => {
    interpret(Machine({id:'test',
      context:body,
      initial: 'start',
      states:{
        start: {
          invoke: {
            src: context => initiate(context),
            onDone: {
              target: 'success'
            }
          }
        },
        success: {
          type: 'final'
        }
      }
    })).onTransition(state => {
      if (state.changed) {
        console.log('state ----> ', state.value);
      }
    })
     .onDone(() => { console.log('done--->'); return resolve()})
     .onStop(() => reject());
     .start();
   }
  });
}
lko
  • 8,161
  • 9
  • 45
  • 62
-1

Add async to your function, then test it again.

exports.handler = async (event, context) => {
    return ...
}
Tuan Vo
  • 1,875
  • 10
  • 10