serverless-plugins icon indicating copy to clipboard operation
serverless-plugins copied to clipboard

serverless-offline-sqs: Cannot create queue with RedrivePolicy

Open jlippitt opened this issue 4 years ago • 10 comments

I'm not sure whether this is an issue with the plugin or the core serverless-offline itself, so apologies if it's the latter. I have an SQS queue with the following Serverless YAML config:

Type: AWS::SQS::Queue
Properties:
  QueueName: '${self:service}-event-source-${self:custom.environment}'
  VisibilityTimeout: '${self:custom.sqs.visibilityTimeout.${self:custom.environment}, self:custom.sqs.visibilityTimeout.default}'
  DelaySeconds: '${self:custom.sqs.delaySeconds.${self:custom.environment}, self:custom.sqs.delaySeconds.default}'
  KmsMasterKeyId: ${self:custom.kmsKeyId}
  RedrivePolicy:
    deadLetterTargetArn: !GetAtt SqsEventSourceDlq.Arn
    maxReceiveCount: '${self:custom.sqs.maxReceiveCount.${self:custom.environment}, self:custom.sqs.maxReceiveCount.default}'

In serverless-offline 5.x with serverless-offline-sqs 3.x, the queue is created and functions correctly. The dead letter queue is not created, but I'm aware there is already an issue open about this (#65), and this is not important to our local setup.

In serverless-offline 6.x with serverless-offline-sqs 4.x, the queue fails to create with the following error (SLS_DEBUG on):

   AWS.SimpleQueueService.NonExistentQueue: AWS.SimpleQueueService.NonExistentQueue; see the SQS docs.
       at Request.extractError (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/protocol/query.js:50:29)
       at Request.callListeners (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
       at Request.emit (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
       at Request.emit (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/request.js:688:14)
       at Request.transition (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/request.js:22:10)
       at AcceptorStateMachine.runTo (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/state_machine.js:14:12)
       at /home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/state_machine.js:26:10
       at Request.<anonymous> (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/request.js:38:9)
       at Request.<anonymous> (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/request.js:690:12)
       at Request.callListeners (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/sequential_executor.js:116:18)
       at Request.emit (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
       at Request.emit (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/request.js:688:14)
       at Request.transition (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/request.js:22:10)
       at AcceptorStateMachine.runTo (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/state_machine.js:14:12)
       at /home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/state_machine.js:26:10
       at Request.<anonymous> (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/request.js:38:9)
       at Request.<anonymous> (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/request.js:690:12)
       at Request.callListeners (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/sequential_executor.js:116:18)
       at callNextListener (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/sequential_executor.js:96:12)
       at IncomingMessage.onEnd (/home/node/app/node_modules/serverless-offline-sqs/node_modules/aws-sdk/lib/event_listeners.js:313:13)
       at IncomingMessage.emit (events.js:203:15)
       at IncomingMessage.EventEmitter.emit (domain.js:448:20)
       at endReadableNT (_stream_readable.js:1143:12)
       at process._tickCallback (internal/process/next_tick.js:63:19)
   From previous event:
       at PluginManager.invoke (/home/node/app/node_modules/serverless/lib/classes/PluginManager.js:498:22)
       at getHooks.reduce.then (/home/node/app/node_modules/serverless/lib/classes/PluginManager.js:533:24)
   From previous event:
       at PluginManager.run (/home/node/app/node_modules/serverless/lib/classes/PluginManager.js:533:8)
       at variables.populateService.then (/home/node/app/node_modules/serverless/lib/Serverless.js:162:33)
   From previous event:
       at Serverless.run (/home/node/app/node_modules/serverless/lib/Serverless.js:149:74)
       at serverless.init.then.then (/home/node/app/node_modules/serverless/scripts/serverless.js:58:26)
       at runCallback (timers.js:705:18)
       at tryOnImmediate (timers.js:676:5)
       at processImmediate (timers.js:658:5)
       at process.topLevelDomainCallback (domain.js:126:23)
   From previous event:
       at Object.<anonymous> (/home/node/app/node_modules/serverless/scripts/serverless.js:58:4)
       at Module._compile (internal/modules/cjs/loader.js:778:30)
       at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
       at Module.load (internal/modules/cjs/loader.js:653:32)
       at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
       at Function.Module._load (internal/modules/cjs/loader.js:585:3)
       at Module.require (internal/modules/cjs/loader.js:692:17)
       at require (internal/modules/cjs/helpers.js:25:18)
       at Object.<anonymous> (/home/node/app/node_modules/serverless/bin/serverless.js:41:1)
       at Module._compile (internal/modules/cjs/loader.js:778:30)
       at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
       at Module.load (internal/modules/cjs/loader.js:653:32)
       at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
       at Function.Module._load (internal/modules/cjs/loader.js:585:3)
       at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
       at startup (internal/bootstrap/node.js:283:19)
       at bootstrapNodeJSCore (internal/bootstrap/node.js:623:3)

Investigating this, we discovered that, if the RedrivePolicy is removed, the queue will be created successfully. This would be a workaround; however, we need the RedrivePolicy when we deploy to production, and it doesn't seem possible to put a condition on a RedrivePolicy.

Essentially, something that used to work with an old version of the plugin is broken with the current one, which means we can't upgrade to the current version. It's also not a configuration error on our side as it works with 'online' Serverless.

jlippitt avatar Jan 28 '21 09:01 jlippitt

We have exactly the same problem! Would appreciate a solution :)

davidjung1977 avatar Mar 16 '21 17:03 davidjung1977

@jlippitt - After a few attempts, I found a temporary solution / workaround for our local environment.

The problem seems to be the "Autostart" function. Maybe a timing issue?

Our solution looks like this:

  serverless-offline-sqs:
    autoCreate: false

Create SQS Queues on Docker startup manually:

aws sqs create-queue --queue-name <YOUR-QUEUE_NAME>-local --endpoint-url http://sqs:9324 --region eu-central-1

davidjung1977 avatar Mar 16 '21 17:03 davidjung1977

Can confirm was having the same issue but @davidjung1977 workaround worked for me.

invaderb avatar Mar 24 '21 16:03 invaderb

This is also affecting us. We're going with the above mentioned workaround temporarily but we're still hoping this get's fixed soon so that we can use "autoCreate: true" as intended.

gduquesnay avatar Aug 03 '21 05:08 gduquesnay

We are facing the same problem, would be nice to have a "real" fix. For now we are going to try out the fix that is mentioned by @davidjung1977

dah-projects avatar Oct 20 '21 16:10 dah-projects

Hi guys, I'm seeing this open conversation and would like to ask you about a problem I'm facing at the moment. Basically, I managed to set up the serverless-offline-sqs locally but it's not working when I run the solution locally in Debugging mode. Has one of you encountered this problem before, please?

nichmica7 avatar Dec 29 '21 14:12 nichmica7

For everyone having this problem and also using elasticmq, instead of the problematic autoCreate: true you can use a config for elasticmq pretty easily: https://github.com/softwaremill/elasticmq#automatically-creating-queues-on-startup

webjunkie avatar Mar 01 '22 13:03 webjunkie

Actually seems like there's no support for SQS Queues with RedrivePolicy from this plugin. My workaround was just to use the serverless-plugin-ifelse to exclude the RedrivePolicy property definition in a local stage. It worked and let me create the SQS Queues i needed. Here's what I did:

serverless.yml

custom:
    serverlessIfElse:
        - If: '"${opt:stage}" == "local"'
          Exclude:
                - resources.Resources.JobProcessQueue.Properties.RedrivePolicy
                - resources.Resources.JobEventProcessQueue.Properties.RedrivePolicy
                
    serverless-offline-sqs:
        autoCreate: true
        apiVersion: "2012-11-05"
        endpoint: http://localhost:9324
        region: us-east-1
        skipCacheInvalidation: false

plugins:
    - serverless-plugin-ifelse
    - serverless-offline-sqs
    - serverless-offline

And voila! It worked perfectly. Hope this help anybody else

djhrcode avatar Mar 28 '22 18:03 djhrcode

We are facing the same problem, would be nice to have a "real" fix. For now we are going to try out the fix that is mentioned by @davidjung1977

+1

sebitoelcheater avatar Jun 17 '22 00:06 sebitoelcheater

If your dead letter queue follows a consistent naming schema you can replace the following function in src/sqs.js:

  create(events) {
    return Promise.all(events.map(({functionKey, sqs}) => this._create(functionKey, sqs)));
  }

with this one:

  create(events) {
    const sortedEvents = events.sort((a, b) => {
      const aContainsDlq = a.functionKey.includes('-dlq-');
      const bContainsDlq = b.functionKey.includes('-dlq-');
      
      return bContainsDlq - aContainsDlq;
    });

    return sortedEvents.reduce((promiseChain, event) => {
      return promiseChain.then(() => this._create(event.functionKey, event.sqs));
    }, Promise.resolve());
  }

Just replace -dlq- with your naming schema.

Zer0x00 avatar Apr 08 '24 17:04 Zer0x00