Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

undefined method `read' for nil:NilClass when calling any endpoint #724

Closed
2 of 3 tasks
enmachs opened this issue Apr 30, 2024 · 1 comment
Closed
2 of 3 tasks

undefined method `read' for nil:NilClass when calling any endpoint #724

enmachs opened this issue Apr 30, 2024 · 1 comment
Labels
bug v5 jets v5 issues

Comments

@enmachs
Copy link

enmachs commented Apr 30, 2024

Checklist

  • Upgrade Jets: Are you using the latest version of Jets? This allows Jets to fix issues fast. There's a jets upgrade command that makes this a simple task. There's also an Upgrading Guide: http://rubyonjets.com/docs/upgrading/
  • Reproducibility: Are you reporting a bug others will be able to reproduce and not asking a question. If you're unsure or want to ask a question, do so on https://community.boltops.com
  • Code sample: Have you put together a code sample to reproduce the issue and make it available? Code samples help speed up fixes dramatically. If it's an easily reproducible issue, then code samples are not needed. If you're unsure, please include a code sample.

My Environment

Software Version
Operating System
Jets 5.0.13
Ruby 3.2.2

Expected Behaviour

Every call to the API should return an ok status even if lambdas

Current Behavior

Calling the api endpoint using api gateway tester:
image

Raises this error in the export controller lambda:

NoMethodError (undefined method `read' for nil:NilClass):
  
app/controllers/export_controller.rb:26:in `update_data'
app/controllers/export_controller.rb:3:in `create'
app/controllers/ExportController.rb:1:in `run'

I was having the same error when deploying but was fixed here

I have found some reference pointing to this method on jets, and looks invoke method for lambda client comes with payload nil or unavailable for some reason.

{
    "errorMessage": "undefined method `read' for nil:NilClass",
    "errorType": "Function<NoMethodError>",
    "stackTrace": [
        "/opt/ruby/gems/3.2.0/gems/jets-5.0.12/lib/jets/commands/call/caller.rb:78:in `remote_run'",
        "/opt/ruby/gems/3.2.0/gems/jets-5.0.12/lib/jets/commands/call/caller.rb:31:in `run'",
        "/opt/ruby/gems/3.2.0/gems/jets-5.0.12/lib/jets/job/base.rb:39:in `perform_later'",
        "/var/task/app/jobs/process_job.rb:30:in `block in aggregate_data'",
        "/opt/ruby/gems/3.2.0/gems/activerecord-7.0.8.1/lib/active_record/relation/delegation.rb:88:in `each'",
        "/opt/ruby/gems/3.2.0/gems/activerecord-7.0.8.1/lib/active_record/relation/delegation.rb:88:in `each'",
        "/var/task/app/jobs/process_job.rb:29:in `aggregate_data'",
        "/var/task/app/jobs/process_job.rb:36:in `story_aggregate_process'",
        "/var/task/app/jobs/process_job.rb:16:in `process_one_story'",
        "/opt/ruby/gems/3.2.0/gems/jets-5.0.12/lib/jets/job/base.rb:28:in `process'",
        "/opt/ruby/gems/3.2.0/gems/jets-5.0.12/lib/jets/exception_reporting.rb:37:in `block in process'",
        "/opt/ruby/gems/3.2.0/gems/jets-5.0.12/lib/jets/exception_reporting.rb:8:in `with_exception_reporting'",
        "/opt/ruby/gems/3.2.0/gems/jets-5.0.12/lib/jets/exception_reporting.rb:36:in `process'",
        "app/jobs/process_job.rb:1:in `run'",
        "/opt/ruby/gems/3.2.0/gems/jets-5.0.12/lib/jets/processors/main_processor.rb:32:in `instance_eval'",
        "/opt/ruby/gems/3.2.0/gems/jets-5.0.12/lib/jets/processors/main_processor.rb:32:in `run'",
        "/opt/ruby/gems/3.2.0/gems/jets-5.0.12/lib/jets/core.rb:171:in `process'",
        "/var/task/handlers/jobs/process_job.rb:9:in `process_one_story'"
    ]
}

Step-by-step reproduction instructions

Code Sample

export_controller.rb:

class ExportController < ApplicationController
  def create
    export_params[:update_data].present? ? update_data : export_csv

    render json: { message: "Export and Updating Data in progress" }
  end

  private

  def export_params
    params.permit(:story_id, :locale, :email, :update_data, :update_chart_data)
  end

  def export_csv
    ExportJob.perform_later(:export, { story_id: export_params[:story_id],
                                       locale: export_params[:locale],
                                       email: export_params[:email] })
  end

  def update_data
    ProcessJob.perform_later(:process_one_story, { story_id: export_params[:story_id],
                                                   locale: export_params[:locale],
                                                   email: export_params[:email] })
  end
end

process_job.rb

# frozen_string_literal: true

class ProcessJob < ApplicationJob
  class_managed_iam_policy(
    'service-role/AWSLambdaBasicExecutionRole'
  )
  def process_data
    Story.aggregated_viewings.find_each do |story|
      story_aggregate_process(story)
    end
  end

  def process_one_story
    story_id = event["story_id"]
    story = Story.includes(:viewings).find_by(id: story_id)
    story_aggregate_process(story)
    { message: "Data processing story: #{story_id} - done" }
  end

  private

  def remove_data(story)
    DataAggregation.where(story_id: story).delete_all
    story.viewings.where(started: true).update_all(aggregated: false)
    story.update_attribute(:update_csv, false)
  end

  def aggregate_data(story)
    viewings = story.viewings
    viewings.where(aggregated: false, started: true).each do |viewing|
      AggregateJob.perform_later(:aggregate_data, { story: story.id, viewing: viewing.id })
    end
  end

  def story_aggregate_process(story)
    remove_data(story) if story.update_csv
    aggregate_data(story)
  end
end

aggregate_job.rb

# frozen_string_literal: true

class AggregateJob < ApplicationJob
  class_reserved_concurrent_executions 10

  def aggregate_data
    perform_data(event)
  end

  private

  def perform_data(event)
    story = Story.find_by(id: event["story"])
    viewing = Viewing.find_by(id: event["viewing"])

    aggregate_data_for_language(story, viewing)
    viewing.update_attribute(:aggregated, true)
  end

  def aggregate_data_for_language(story, viewing)
    story.transcription_languages.each do |language|
      csv_row, single_row, multi_row = Export::Answers.new(story: story, language: language, viewing: viewing ).generate_csv_line
      data_aggregation = build_update_attributes(viewing, language, csv_row, single_row, multi_row, story)
      DataAggregation.update_or_create_by({ viewing_id: viewing.id, locale: language }, data_aggregation)
    end
  end
  def build_update_attributes(viewing, language, csv_row, single_row, multi_row, story)
    {
      story_id: story.id,
      viewing_id: viewing.id,
      data_order: viewing.start_time,
      multi_choice_data: multi_row.to_csv,
      single_choice_data: single_row.to_csv,
      data: csv_row.to_csv,
      locale: language
    }
  end
end

config/routes.rb

Jets.application.routes.draw do
  root "jets/welcome#index"

  get 'aggregate', to: 'aggregate#process'
  resources :export, only: [:create]

  # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
  # Can be used by load balancers and uptime monitors to verify that the app is live.
  get "up" => "jets/health#show", as: :jets_health_check

  # The jets/public#show controller can serve static utf8 content out of the public folder.
  # Note, as part of the deploy process Jets uploads files in the public folder to s3
  # and serves them out of s3 directly. S3 is well suited to serve static assets.
  # More info here: https://rubyonjets.com/docs/extras/assets-serving/
  any "*catchall", to: "jets/public#show"
end

config/application.rb

module ExportData
  class Application < Jets::Application
    config.load_defaults 5.0

    config.project_name = "export-data"
    config.mode = "api"

    config.prewarm.enable = true

    config.controllers.default_protect_from_forgery = false

    if Jets.env == 'production'
      config.function.vpc_config = {
        security_group_ids: ENV['SECURITY_GROUP_IDS'].split(','),
        subnet_ids: ENV['SUBNET_IDS'].split(',')
      }
    end
    # Docs:
    # https://rubyonjets.com/docs/config/
    # https://rubyonjets.com/docs/config/reference/
  end
end

Solution Suggestion

Since it looks it's for logging purpose, we could validate response would be validate payload it's not nil before trying to access it.

@enmachs enmachs added the bug label Apr 30, 2024
@tongueroo tongueroo added the v5 jets v5 issues label May 24, 2024
@tongueroo
Copy link
Collaborator

Don't think this applies anymore with the release of Jets 6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug v5 jets v5 issues
Projects
None yet
Development

No branches or pull requests

2 participants