Postgresql 에도 여러 제어문이 존재한다. IF, FOR 같은 구문이 존재한다.  (https://www.postgresql.org/docs/current/plpgsql-control-structures.html#PLPGSQL-RECORDS-ITERATING)
그런데 실제 동작시켜보면 에러가 발생한다. 보통 이 구문 예제에는 함수에서 등작하는 편이다. 그래서 이 것은 함수 밖에 동작하지 않는다고 생각하고 있었다. 그런데 do 라는 것과 같이 사용할 수 있다는 것을 알게 되었다.

참고: Postgresql DO 구문 ( https://www.postgresql.org/docs/current/sql-do.html)

 code block 에 대해 찾아보면 딱히 설명이 메뉴얼에 안 나와있는데, 이상하게 DO 구문 설명에서는 "anonymous code blocks" 이라는 단어가 나온다. 내 추측으로는 저 설명은 Structure of PL/pgSQL(https://www.postgresql.org/docs/current/plpgsql-structure.html)을 말하는 것 같다. 

[ <<label>> ]
[ DECLARE
    declarations ]
BEGIN
    statements
END [ label ];

이 형태는 일반적으로 function 을 정의할 때 사용되는데 DO 라는 구문과 조합할 수 있다. 

이 때 https://www.postgresql.org/docs/current/plpgsql-control-structures.html#PLPGSQL-CONDITIONALS 에기 나온 control 구문을 조합할 수 있다. 

여기에 더해서 Postgresql 에서는 마치 print 처럼 사용할 수 있는 구문이 있다. 

DO
$$ BEGIN
    raise notice 'Hello world';
END $$;

이 구문을 이용하면 'Hello world' 를 출력할 수 있다. psql 나 dbeaver 에서 이것을 출력할 수 있다. 

그래서 IF 문 다음과 같이 만들 수 있다.

DO
$$
DECLARE
  a INTEGER := 10;
  b INTEGER := 20;
  result INTEGER := 0;
BEGIN
  IF a > b THEN
    result := 1;
  ELSIF a < b THEN 
    result := -1;
  ELSE
    result := 0;
  END IF;  

  raise notice 'result : %', result;
END;
$$ LANGUAGE plpgsql;


그리고  아래 같은 For 문도 만들 수 있다.

DO
$$ 
DECLARE
  n INTEGER := 10;
  total INTEGER := 0;
BEGIN
  FOR i IN 1..n LOOP
    total := total + i;
  END LOOP;
  raise notice 'total : %', total;
END;
$$ LANGUAGE plpgsql;

 

 

 이 글을 작성한지 꽤 오랜 시간이 흐른 후 읽는 사람이 있을 것 같다. 그리고 나와는 조금 다른 환경에서 다른 라이브러리가 필요한 일이 있을 것이다. 미래에는 lambda 기능 자체가 업데이트 되면서 변경될 수도 있다. 내가 문제를 해결한 방식을 잘 응용하면 해당 문제를 푸는데 도움일 될 수도 있을 것이다. 

aws 계층에 업로드된 파일의 경로 확인하기

내 경우는 python.zip 파일을 계층에 업로드 할 때 이 경로가 aws lambda 에서는 /opt/python 이었다. 이 경로가 어떻게 결정되는 것인지 모르겠지만 확인하는 방법이 있다. 

import requests
print(requests.__file__)

위 코드를 aws lambda 에 실행시켜 본다. 내 경우에는 /opt/python/requests/init.py  이라는 경로가 나왔다. __file__ 이라는 키워드는 파이썬 코드에서 사용이 가능하지만 module 에 대해서 경로를 확인하는 용도로도 사용이 가능하다. 


필요로 하는 shared_library 알기

참고 :  https://stackoverflow.com/a/47346043    
이번 작업에서 가장 어려웠던 일 중의 하나이다. https://aws.amazon.com/ko/blogs/compute/creating-faster-aws-lambda-functions-with-avx2/  에서 나왔던  파일들은 모든 파일을 포함되어 있지 않았다. 그래서  /usr/lib64 을 다 넣을까 하는 생각도 해봤지만 그렇기에는 zip 파일이 크게 나왔다. 어째든 stackoverflow 의 글을 잘 참고하면 된다. 그런데 lsof  가 없기 때문에 yum install lsof   로 설치해야 한다. 설치하고 나서 lsof 를 이용할 때는 /usr/sbin/lsof   라고 해야 한다. 기본 path 설정에 /usr/sbin 이 없는 것 같다. 어째든 저 방법을 이용해서 필요한 so 파일을 알 수 있다. 그리고 이 동작을 하기 위해서는 2개의 bash 터미널이 필요하다. 기존의 docker  container 에서 2개의 bash 터미널을 준비한다. 2개의 터미널을 각각 consoleA와 consoleB라고 하겠다. 

#### consoleA ####
# 파이썬 특정 경로 라이브러리 이용해서 실행
PYTHONPATH="/root/python" python3
>>> import os
>>> os.getpid()

아래 코드 동작전 consoleB의 before 파일 생성할 것

>>> from PIL import Image

위 코드 동작후 consoleB의 after 파일 생성할 것

############

#### consoleB ####
# os.getpid() 에서 나온 번호를 이용한다. 여기서는 1093 이라고 가정한다.
/usr/sbin/lsof  -p 1093 > before 

consoleA 에서 from PIL import Image 동작 후 
/usr/sbin/lsof  -p 1093 > after


# from PIL import Image 동작 후 추가된 so 파일 확인 하기 위해 
# before 파일과 after 파일 비교
diff <(awk '{print $NF}' after) <(awk '{print $NF}' before)

# 출력된 so 파일들이 로딩된 shared library 임

pillow-simd 의 경우 운이 좋아 from PIL import Image  할 때 shared library 가 호출 되었다. 경우에 따라서는 webp 파일을 불러올 때 특정 webp 라이브러리를 이용하는 식으로 얼마든지 lazy 하게 library 를 가져올 수도 있다. 이 경우는 이런 것들 동작까지 한 후 어떤 so 파일이 로딩되는지 확인해야 한다.

 

소스에 업로드된 파일 읽기 불가

내 경우에는 thumbnail 이미지를 만들때, png파일을 이용해서 watermark 를 붙인다. 이 watermark 이미지를 lambda 코드안에 파일로 업로드 했다. (python.zip 폴더 아니고 소스를 수정하는 vscode 화면에 직접 파일을 업로드 했음) 그런데 여기 올라온 이미지파일을 pillow로 열면 에러가 발생했다. 처음에는 png 문제라고 생각했는데, 웃기게도 aws s3 에 있으면 잘 읽어졌다. 파일사이즈나 데이터자체도  aws lambda 코드로 확인해봤는데 정상적이었다. 나 처럼 고생하지 말고 이런 현상 있으면 그냥 s3 를 이용하자. 


트리거 중복으로 추가할 수 없다고 에러 발생할 때

aws lambda 함수에 트리거를 걸어 넣고, 그냥 삭제해 버리면 이런 현상이 있다. s3 버킷 속성에 해당 트리거가 남아 있다. 해당 트리거를 지우도록 하자. 

 

 

기본적인 사항(권한설정, 트리거)은
자습서: Amazon S3 트리거를 사용하여 썸네일 이미지 생성 https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/with-s3-tutorial.html  을 참고하면 된다. 

 앞에서 만든 python.zip 파일을 계층에 업로드 해야 한다. 호환 아키텍처은 x86_64  로 설정한다. 

그리고 pillow-simd 을 설치하는 경우 꼭 환경변수를 설정해야 한다. 
LD_LIBRARY_PATH  에 대해
/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib:/opt/python/lib
라고 설정해야 한다. 

LD_LIBRARY_PATH  는 shared library 경로를 지정하는 환경변수이다. 사실 일반적인 경우 

$LD_LIBRARY_PATH:/opt/python/lib

이런 식으로 기존환경변수에 concat 개념으로 추가할 수 있어야 한다. 그런데 내가 뭘 잘못했는지 잘 안된다. 그래서 좀 무식하게 저렇게 설정했다. 맨 끝에 붙은 /opt/python/lib  가 중요하다. 내가 올린 python.zip 파일이 이 경로에 풀린다는 것이다.  docker에서   /root/python/lib 에 넣었던 파일이 /opt/python/lib 에 위치하게 된다.  당연히  docker에서  /root/python 에서 넣었던 라이브러리는 /opt/python 에 위치하게 될 것이다. 
  어떻게 /opt/python 에 있는 라이브러리가 불러 올 수 있을 까 하는 의문이 들 수도 있다. PYTHONPATH  라는 환경변수를 이용하면 특정경로에 있는 라이브러리를 참고해서 python을 실행할 수 있다. 
 지난 글에서 사용했던 docker에서 

PYTHONPATH="/root/python" python3

라고 환경변수를 전달하면서 python 을 실행하면 /root/python 에 설치했던 라이브러리를 이용해서 파이썬을 실행할 수 있다. 


다음 글에서는 여러문제가 생겼을 때 대처하는 방법에 대해 적겠다. 누군가는 이 글을 나와 같은 버전과 같은 라이브러리를 이용하지 않고 유사 문제를 풀려고 할 수 있다. 내가 이글을 작성하면서 적용한 테크닉을 잘 활용하면 도움이 될수도 있을 것 같다.