뒤끝펑션 - 예측불가능한 오류

  • 뒤끝 SDK 버전 : 뒤끝펑션 0.3.0
  • 프로젝트명 : FlyDochi
  • 스테이터스 코드 :
  • 에러 코드 :
  • 에러 메시지 : Null Return
using Amazon.Lambda.Core;
using BackEnd;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]

namespace BackendFunction
{
    public class ITEM_INFO
    {
        public int id;
        public int amount;
    }

    public class DochiInfo
    {
        public int id;
        public int advancedvalue;
    }

    public class BFunc
    {
        // Static initialization for reuse across invocations
        static bool isInitialized = false;

        public Stream Function(Stream stream, ILambdaContext context)
        {
            if (!isInitialized)
            {
                try
                {
                    // Initialize BackendFunction API
                    Backend.Initialize(ref stream);
                    isInitialized = true;
                }
                catch (Exception e)
                {
                    // Return the reason if Initializing BackendFunction API was failed
                    return ReturnErrorObject("Initialization failed: " + e.ToString());
                }
            }

            var content = Backend.Content;

            // Validate input parameters
            if (!content.ContainsKey("itemCode") || !content.ContainsKey("puchEntity"))
            {
                return ReturnErrorObject("Missing parameters");
            }

            if (!int.TryParse(content["itemCode"].ToString(), out int itemCode))
            {
                return ReturnErrorObject("Invalid itemCode");
            }

            var puchEntity = content["puchEntity"];
            if (puchEntity.Count < 2 || !int.TryParse(puchEntity[0].ToString(), out int upgradeLevel))
            {
                return ReturnErrorObject("Invalid puchEntity");
            }

            var tokens = puchEntity[1];
            int tokenCount = tokens.Count;
            int[] token_ids = new int[tokenCount];
            int[] token_values = new int[tokenCount];

            for (int i = 0; i < tokenCount; i++)
            {
                if (!int.TryParse(tokens[i][0].ToString(), out token_ids[i]) || !int.TryParse(tokens[i][1].ToString(), out token_values[i]))
                {
                    return ReturnErrorObject("Invalid token data");
                }
            }

            DochiInfo dochi = GetDochiInfo(itemCode);
            if (dochi == null)
            {
                return ReturnErrorObject("Item not found");
            }

            Dictionary<int, ITEM_INFO> itemValues = GetItemValues(token_ids);
            if (!ValidateItemValues(itemValues, token_ids, token_values))
            {
                return ReturnErrorObject("Not enough items");
            }

            UpdateItemValues(itemValues, token_ids, token_values);
            dochi.advancedvalue += 1;

            UpdateDochiInfo(dochi);
            return Backend.StringToStream("BackendFunction");
        }

        private DochiInfo GetDochiInfo(int itemCode)
        {
            var where = new Where();
            where.Equal("Code", itemCode);
            var bro = Backend.GameData.GetMyData("Dochi_Inventory", where, new string[] { "Code", "advancedvalue" });

            if (!bro.IsSuccess() || bro.FlattenRows().Count < 1)
            {
                return null;
            }

            var row = bro.FlattenRows()[0];
            return new DochiInfo
            {
                id = int.Parse(row["Code"].ToString()),
                advancedvalue = int.Parse(row["advancedvalue"].ToString())
            };
        }

        private Dictionary<int, ITEM_INFO> GetItemValues(int[] token_ids)
        {
            var values = new Dictionary<int, ITEM_INFO>();
            foreach (int id in token_ids.Distinct())
            {
                var where = new Where();
                where.Equal("id", id);
                var bro = Backend.GameData.GetMyData("itemInventory_table", where); //n번
                if (bro.IsSuccess() && bro.FlattenRows().Count > 0)
                {
                    var row = bro.FlattenRows()[0];
                    values[id] = new ITEM_INFO
                    {
                        id = int.Parse(row["id"].ToString()),
                        amount = int.Parse(row["amount"].ToString())
                    };
                }
            }
            return values;
        }

        private bool ValidateItemValues(Dictionary<int, ITEM_INFO> itemValues, int[] token_ids, int[] token_values)
        {
            for (int i = 0; i < token_ids.Length; i++)
            {
                if (!itemValues.TryGetValue(token_ids[i], out ITEM_INFO item) || item.amount < token_values[i])
                {
                    return false;
                }
            }
            return true;
        }

        private void UpdateItemValues(Dictionary<int, ITEM_INFO> itemValues, int[] token_ids, int[] token_values)
        {
            for (int i = 0; i < token_ids.Length; i++)
            {
                itemValues[token_ids[i]].amount -= token_values[i];
            }

            foreach (var item in itemValues.Values)
            {
                var where = new Where();
                where.Equal("id", item.id);
                var param = new Param();
                param.Add("id", item.id);
                param.Add("amount", item.amount);
                Backend.GameData.Update("itemInventory_table", where, param);
            }
        }

        private void UpdateDochiInfo(DochiInfo dochi)
        {
            var where = new Where();
            where.Equal("Code", dochi.id);
            var param = new Param();
            param.Add("Code", dochi.id);
            param.Add("advancedvalue", dochi.advancedvalue);
            Backend.GameData.Update("Dochi_Inventory", where, param);
        }

        static Stream ReturnErrorObject(string err)
        {
            JObject error = new JObject();
            error.Add("error", err);

            return Backend.JsonToStream(error.ToString());
        }
    }
}

해당 데이터는 캐릭터 업그레이드관련 함수들입니다. 근데 여기서 캐릭터관련 아이템코드를 전달할때 분명 인벤토리에 해당 캐릭터가 있는데 없다고 출력되어 "Item Not Found"가 호출되었습니다.

DochiInfo dochi = GetDochiInfo(itemCode);
            if (dochi == null)
            {
                return ReturnErrorObject("Item not found");
            }

오류 호출부분은 이부분입니다.

그런데 유니티(클라이언트)쪽에서는 아이템코드나 재료를정확하게 전부 보냈습니다.
그러나 → 검증단계에서 해당 오류가 발생했었습니다.

그런데 뒤끝펑션 함수를 삭제하고 다시 재업로드하니 갑자기 또 동작이되었습니다.

혹시 이런문제가 어떤경우에 발생하는지 예측이 가시는게 있습니까?

안녕하세요, 개발자님.
문의하신 내용은 확인 후 안내드리도록 하겠습니다.
시간 양해 부탁드립니다.

안녕하세요 개발자님,

GetMyData() 를 통한 데이터 조회 시 where.Equal(Code, codeNumber) 에 결과가 없어서 발생하는 상황으로 예상되며, 해당 상황은 다음과 같은 경우에 발생할 수 있습니다.

  1. GetMyData를 통해 조회하는 데이터가 많을 경우 조건에 맞는 데이터를 찾는데 오랜 시간이 소요되어 타임아웃이 발생하는 경우
  2. 다른 유저 데이터 검색하는데 GetMyData를 사용하는 경우
  3. 자료형을 잘못 넣어 where절 검색하는 경우

또한 간혈적으로 발생했다 안했다하는 것은 펑션 코드 바뀐게 없다면,
게임 플레이 과정에서 code 데이터가 바뀌어서 검색에 실패하는 상황일 수 있는 것으로 보입니다.

문의를 남겨주신 당시의 정보를 상세히 확인해보고자 하였으나,
프로젝트 내 문의 이전 호출을 진행한 유저 정보가 모두 삭제된 상태로 확인됩니다.

혹 동일 상황이 발생하시는 경우 당시 호출의 유저 정보나 시간정보등을 함께 공유하여 주시면 보다 상세히 확인하여 안내드릴 수 있도록 하겠습니다.

추가로, 뒤끝에서는 테이블별로 유저당 1개의 row를 사용하는 것을 권장하고 있습니다.
지금과 같이 여러 row를 사용하며 where절 등을 사용하시는 조건에 맞는 데이터를 찾는 과정에서 DB 조회의 처리량(사용량)이 많아지면서 요금 증가로 이어질 수 있으니 참고하여 주시면 감사하겠습니다.

그렇군요.

제생각이지만 타임아웃 확률이 매우 높은것 같습니다. 뒤끝펑션 뿐만이 아닌, 클라이언트 GetmyData 과정에서도 동일한 문제가 발생했습니다.

그렇다면 예를들어 인벤토리테이블을 스키마정의로 만들거면 list라는 데이터타입을 최대한 활용해야겠군요.

좋아요 1