Don't know how to literal-quote value ~ : sqlalchemy에게 이 문자를 어떻게 컴파일 해야 하는지 알려줘

 

결론부터 말하자면 Sqlalchemy 1.3.6 이상에서는 일어나지 않는 문제이다.

🔗 sqlalchemy changelog 링크 

 

문제 원인

# ============== example ==============

query = db.session.query(m.Account).filter(m.Account.created_datetime == datetime.now())

query.statement.compile(
	dialect=postgresql.dialect(),
	compile_kwargs={'literal_binds': True},
)

statement를 컴파일 할 때 생기는 문제인데,

Sqlalchemy 1.3.6 버전 이하의 postgresql에서 DateTime을 처리하는 literal processor가 구현되어 있지 않아 생긴 문제로 보인다.

문제 해결

sqlalchemy 창시자 mike bayer 가 손수 해결해줬다.

모든 데이터베이스 엔진에 대해 Datetime객체를 ISO 8601 포멧으로 치환해주도록 변경한 듯 하다. > 🔗 commit log

 

https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/3744/6/lib/sqlalchemy/sql/sqltypes.py#703

 

gerrit.sqlalchemy.org

 

메타클래스에 대해 알아보기 이전에 파이썬의 데이터 모델에 대한 이해가 필요하다.

파이썬에서 모든 것은 데이터를 추상화 한 객체로 이루어져 있다.
또한, 파이썬의 객체는 아이덴티티, 값, 타입을 가지고 있다.

아이덴티티 (id)

id() 함수를 통해 얻을 수 있으며 객체의 수명동안 유일하고 불변함이 보장되는 정수다.

값 (value)

객체의 타입에 따라 불변할 수 있고 가변할 수도 있다. ex)tuple : 불변, list : 가변

타입 (type)

객체가 지원하는 연산들과 그 타입의 객체가 가질 수 있는 값(ex) int : 1, list : [1,2])들을 통해 객체의 특성을 정의한다. 객체의 타입은 type()을 통해 얻을 수 있으며, 불변하다.

여기서 말한 타입과 같이 파이썬의 모든 객체들은 어떠한 타입에 의해 정의된다.

파이썬의 type() 빌트인 함수를 사용하면 객체의 타입을 알 수 있다.

class Test:
    pass
t = Test()
type(t)
# <class '__main__.Test'>

def hello():
    pass
type(hello)
# <class 'function'>

type(1)
# <class 'int'>

위 예제를 보면 Test 클래스의 인스턴스인 tTest가 타입이고, hello 함수는 function이 타입이며, 정수 1은 int가 타입이다.

그렇다면, Test 클래스의 타입은 존재할까?

class Test:
    pass
type(Test)
# <class 'type'>

놀랍게도, Test 클래스 객체의 타입이 존재한다.
여기서 출력된 typeTest 클래스의 메타 클래스라고 하며, 인스턴스로 클래스를 가진다.

그러면 메타클래스는 무슨 용도로 사용하는 걸까?

그전에 몇가지 메타클래스의 매직 메소드에 대해 알아보자

class TestMeta(type):
    def __prepare__(mcs, *args, **kwarg): # 메타 클래스가 결정되었을 때 (mro가 구성된 후) 클래스 정의를 위해 호출된다.
        # mcs = metaclass
        print("__prepare__()")
        return super.__prepare__(mcs, *args, **kwarg)

    def __new__(mcs, *args, **kwargs):  # 클래스를 생성 할 때 호출됨
        # mcs = metaclass
        print("__new__()")
        return super().__new__(mcs, *args, **kwargs)

    def __init__(cls, *args, **kwargs):  # 클래스가 생성 된 후 호출됨
        print("__init__()")
        super().__init__(*args, **kwargs)

    def __call__(cls, *args, **kwargs):  # 클래스의 인스턴스를 생성할 때 호출됨
        print("__call__()")
        return super().__call__(*args, **kwargs)


class Test(metaclass=TestMeta):
    pass

# __prepare__()
# __new__()
# __init__()

t = Test()
# __call__()

위 코드를 보면 __prepare__, __new__, __init__, __call__ 메소드를 작성하고 사용하는 것을 볼 수 있다.

  • __prepare__ 메소드는 메타 클래스가 결정되었을 때 호출되며, 클래스의 네임 스페이스를 준비한다.
  • __new__ 메소드는 클래스 객체를 생성 할 때 호출된다.
  • __init__ 메소드는 클래스가 생성 된 후 호출되어 클래스를 초기화 한다.
  • __call__ 메소드는 클래스의 인스턴스를 생성 할 때 호출 된다.

이 매직 메서드를 가지고 무슨 일을 할 수 있을까?

싱글톤 패턴 구현

싱글톤 패턴은 클래스의 인스턴스화를 항상 하나의 개체로만 제한하는 설계 패턴이다.
구현해 보자면

class SingletonMeta(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]


class SingletonClass(metaclass=SingletonMeta):
    pass

sl1 = SingletonClass()
sl2 = SingletonClass()

print(id(sl1))
# 1877212284048
print(id(sl2))
# 1877212284048

인스턴스 생성에 관여하는 __call__()메소드를 오버라이딩 해서 클래스를 key로 두고 인스턴스를 value로 만들어 클래스당 하나의 인스턴스를 가지도록 했다.

애트리뷰트 검증

DRF의 ModelSerializer 에는 내부 Meta 클래스가 없다면 오류를 일으킨다. 이에 대한 오류 검증을 DRF에서는 get_fields() 메소드에 구현해 두었는데, 이를 메타 클래스로 검증 할 수 있을 것 같다.

def get_fields(self):
    ...

    assert hasattr(self, 'Meta'), (
        'Class {serializer_class} missing "Meta" attribute'.format(
            serializer_class=self.__class__.__name__
        )
    )
    ...

위 코드는 클래스 내부에 Meta 애트리뷰트가 있는지 확인하는 코드이다. 이를 메타클래스로 검증하는 코드를 짜보자

class ModelSerializerMetaclass(SerializerMetaclass):
    def __new__(mcs, *args, **kwargs):
        name, bases, namespace = args
        if name not in ("ModelSerializer","HyperlinkedModelSerializer"):
            mcs._check_meta(name,namespace)

        return super().__new__(mcs, *args, **kwargs)

    def _check_meta(name,namespace):
        if not namespace.get("Meta", None):
            raise Exception(f'Class {name} missing "Meta" attribute')
        return


class ModelSerializer(Serializer, metaclass=ModelSerializerMetaclass):
    pass

RDBMS에 친숙하다면

  • 정규화가 잘 된 데이터모델의 유용함
  • 트랜잭션의 필요성
  • 견고한 저장 엔진을 통해 얻는 확신

을 잘 알고 있을 것이다.

이런 RDBMS를 벗어나 NoSQL로 옮긴다면 어떤 장점이 있을까?

  1. 확장의 용이성MongoDB의 데이터 모델과 지속성 전략은 높은 읽기/쓰기 효율자동 장애조치를 통한 확장의 용이성을 염두에 두고 만들어졌다.
  2. MongoDB는 웹 애플리케이션과 인터넷 기반을 위해 설계된 데이터베이스 관리 시스템이다.
  3. 직관적인 데이터 모델예를 들자면 아래에 사용자에 대한 정보를 몇 개의 필드로 저장한 도큐먼트가 있다.만약 하나의 사용자가 여러개의 이메일을 가질 수 있다면 어떻게 해야할까?하지만 MongoDB는 아래와 같이 사용하면 된다.예시와 같이 MongoDB는 한명의 사람 정보를 얻기 위해서 하나의 도큐먼트로 표현할 수 있다.
  4. 만약 RDBMS였다면 여러개의 테이블에 나눠져 있는 정보를 조인 연산으로 가득찬 SQL 쿼리를 만들어 표현했어야 할 것이다.
  5. { _id: 10, username: 'hanbin', email : [ 'hanbin8269@gmail.com', 'gksqls0128@naver.com' ] }
  6. RDBMS라면 서로 조인을 하기 위해 이메일 과 사용자 테이블을 각각 만들어야 할 것이다.
  7. { _id: 10, username: 'hanbin', email : 'hanbin8269@gmail.com' }
  8. MongoDB는 데이터의 변경이 발생했을 때 스키마에 맞춰야 하는 걱정 없이 구조화 된 도큐먼트를 저장할 수 있다는 장점이 있는데,
  9. 객체 매핑의 단순화MongoDB의 도큐먼트는 JSON에 기반하는데, JSON은 프로그래밍 언어에서 dictionary나 hash map과 유사하다.
  10. 그렇기 떄문에 프로그래밍 언어에서 정의한 객체가 그대로 저장되므로 객체 매퍼의 복잡성이 사라진다.
  11. 개발자들은 주로 객체지향언어를 사용해 작업을 하기 때문에 객체에 잘 매핑되는 데이터 저장 구조를 원한다.

MongoDB는 도큐먼트 지향적인 데이터베이스다.

내부적으로 MongoDB는 Binary JSON의 형태로 도큐먼트를 저장한다.

 

MYSQL은 데이터를 테이블에 행으로서 저장하는 반면

MongoDB가 도큐먼트의 모음과 같이 데이터를 컬렉션에 도큐먼트로 저장한다.

 

MYSQL와 같은 RDBMS는 한 객체의 데이터를 여러개의 테이블로 나누어 표현하는 정규화를 거치는데, 정규화를 너무 많이 한다면 그에 따른 비용이 들어가게 된다.

이는 곧 데이터를 모으는 작업을 해야 한다는 것이다.

예를 들어서 블로그 글 하나를 보여주기 위해서 post와 comment 테이블에 대한 조인 연산이 필요해 진다.

하지만 도큐먼트 지향적인 데이터 모델(MongoDB)에서는 객체를 자연스럽게 모아놓은 형태로 표현함으로 객체를 전체적으로 작업할 수 있다는 점이 있다.

위에서 post 와 comment 등의 블로그 글 하나를 표현하는 모든 데이터 들이 하나의 데이터베이스 객체에 들어갈 수 있다.

스키마가 없는 모델

도큐먼트에는 미리 정해진 스키마가 없다. RDBMS에서는 데이터 열을 테이블에 저장하지만, 각 테이블은 각각의 컬럼에 대해 허용되는 데이터 타입을 명시한다.

이렇게 스키마가 없다는 것은 아래와 같은 장점이 있다.

첫번째로 데이터베이스가 아닌 애플리케이션이 데이터구조를 정한다는 것이다. 이것은 데이터의 구조가 빈번히 변경되는 개발 초기단계에서 개발 속도를 단축시켜 준다.

두번째로 가변적인 속성을 갖는 데이터를 표현할 수 있다는 것이다. 예를 들어 상품 판매 사이트를 만든다고 했을때, 하나의 상품이 어떤 속성을 갖게 될 지 미리 알 수 없으므로 애플리케이션에서는 이러한 가변적인 속성을 처리할 수 있어야 한다.

이런점에서 MongoDB는 새로운 속성은 도큐먼트에 동적으로 처리할 수 있기 때문에 추후에 필요한 데이터필드가 무엇일지 고민할 필요가 없다.

고정된 스키마를 갖는 데이터베이스에서는 EAV(Entity-Attribute-Value) 패턴을 이용한다.

Entity-Attribute-Value model?

데이터베이스 설계 시 오브젝트의 자유도를 최대한 보장해야 하는 경우 고려 가능한 모델

잠재적인 어트리뷰트는 많지만 실제 사용되는 어트리뷰트 수는 적을 떄 사용되는 데이터 모델이다.

  • Entity는 테이블에서의 엔터티 객체를 나타낸다.
  • Attribute는 객체의 어트리뷰트(Field)이다.
  • Value는 객체의 어트리뷰트의 값이다.

애드혹 쿼리

**애드혹 쿼리(ad hoc query)**란 아래 예제처럼 미리 정의된 쿼리가아닌 아래와 같이 파라미터가 늘 변하게되는 변수로 작용하는 되는 쿼리이다. (**user_id**가 매번 변함)

query = "SELECT * FROM users WHERE id=" + user_id

RDBMS만 사용해 봤다면 애드혹 쿼리를 당연한 것으로 생각하기 쉽지만, 사실 모든 데이터베이스가 이런 동적 질의를 지원하는 것은 아니다.

다른 많은 시스템에서 간단하고 확장성 높은 모델을 위해 풍부한 쿼리 기능을 포기하는 경우가 많은데,

MongoDB의 설계 목표 중 하나는 관계형 데이터베이스상에서 매우 필수적은 쿼리 성능을 유지하는 것이다.

MongoDB에서 쿼리 언어는 아래와 같이 작동한다.

db.posts.find({'tags': 'politics', 'vote_count': {'$gt': 10}})

SQL 쿼리는 엄격하게 정규화 된 모델에 의존하는 반면, MongoDB의 쿼리는 태그가 각 도큐먼트에 포함되어 있다고 가정한다.

여러개의 속성 (tags 검색, vote_count 검색)을 조합하여 질의할 수 있는 능력을 보여주는데, 이것이 애드혹 쿼리의 본질적인 강점이다.

인덱스

MongoDB에서 인덱스는 B-Tree(B-Tree?) 로 구현되어 있다.

관계 데이터베이스에서 기본적으로 제공하는 기능인 인덱스는 범위를 스캔하거나 정렬을 하는 것과 같이 간단한 쿼리에 대해 최적화 되어 있다.

하지만 WiredTiger는 LSM tree(Log-Structed Merge-trees)를 지원한다고 한다.

B-Tree?

이진 트리가 자식 노드가 최대 2개인 노드를 말하는 것이라면 B-Tree는 자식 노드의 개수가 2개 이상인 트리를 말한다.

또한 노드내의 데이터가 1개 이상일 수가 있다. 만약 노드 내 최대 데이터 수가 2개라면 2차 B-Tree, 3개라면 3차 B-Tree라고 말한다.

그리고, 노드의 데이터 수가 N개라면 자식 노드의 개수는 N+1개 이다.

또한, 6보다 작은 값은 왼쪽 서브트리, 6과 17 사이의 값은 중간 서브트리, 17보다 큰 값은 오른쪽 서브트리에 들어가는 구조를 볼 수 있다.

탐색법은 작은 값은 왼쪽, 큰값은 오른쪽에 있기 때문에 root 노드부터 시작해 하향식으로 탐색해 나간다.

내부적으로 MongoDB는 Binary JSON의 형태로 도큐먼트를 저장한다.

BSON(Binary JSON)은 무엇인가?

MYSQL은 데이터를 테이블에 행으로서 저장하는 반면

MongoDB가 도큐먼트의 모음과 같이 데이터를 컬렉션에 도큐먼트로 저장한다.

MYSQL와 같은 RDBMS는 한 객체의 데이터를 여러개의 테이블로 나누어 표현하는 정규화를 거치는데, 정규화를 너무 많이 한다면 그에 따른 비용이 들어가게 된다.

이는 곧 데이터를 모으는 작업을 해야 한다는 것이다.

예를 들어서 블로그 글 하나를 보여주기 위해서 post와 comment 테이블에 대한 조인 연산이 필요해 진다.

하지만 도큐먼트 지향적인 데이터 모델(MongoDB)에서는 객체를 자연스럽게 모아놓은 형태로 표현함으로 객체를 전체적으로 작업할 수 있다는 점이 있다.

위에서 post 와 comment 등의 블로그 글 하나를 표현하는 모든 데이터 들이 하나의 데이터베이스 객체에 들어갈 수 있다.

스키마가 없는 모델

도큐먼트에는 미리 정해진 스키마가 없다. RDBMS에서는 데이터 열을 테이블에 저장하지만, 각 테이블은 각각의 컬럼에 대해 허용되는 데이터 타입을 명시한다.

이렇게 스키마가 없다는 것은 아래와 같은 장점이 있다.

첫번째로 데이터베이스가 아닌 애플리케이션이 데이터구조를 정한다는 것이다. 이것은 데이터의 구조가 빈번히 변경되는 개발 초기단계에서 개발 속도를 단축시켜 준다.

두번째로 가변적인 속성을 갖는 데이터를 표현할 수 있다는 것이다. 예를 들어 상품 판매 사이트를 만든다고 했을때, 하나의 상품이 어떤 속성을 갖게 될 지 미리 알 수 없으므로 애플리케이션에서는 이러한 가변적인 속성을 처리할 수 있어야 한다.

이런점에서 MongoDB는 새로운 속성은 도큐먼트에 동적으로 처리할 수 있기 때문에 추후에 필요한 데이터필드가 무엇일지 고민할 필요가 없다.

고정된 스키마를 갖는 데이터베이스에서는 EAV(Entity-Attribute-Value) 패턴을 이용한다.

Entity-Attribute-Value model?

데이터베이스 설계 시 오브젝트의 자유도를 최대한 보장해야 하는 경우 고려 가능한 모델

잠재적인 어트리뷰트는 많지만 실제 사용되는 어트리뷰트 수는 적을 떄 사용되는 데이터 모델이다.

  • Entity는 테이블에서의 엔터티 객체를 나타낸다.
  • Attribute는 객체의 어트리뷰트(Field)이다.
  • Value는 객체의 어트리뷰트의 값이다.

애드혹 쿼리

**애드혹 쿼리(ad hoc query)**란 아래 예제처럼 미리 정의된 쿼리가아닌 아래와 같이 파라미터가 늘 변하게되는 변수로 작용하는 되는 쿼리이다. (**user_id**가 매번 변함)

query = "SELECT * FROM users WHERE id=" + user_id

RDBMS만 사용해 봤다면 애드혹 쿼리를 당연한 것으로 생각하기 쉽지만, 사실 모든 데이터베이스가 이런 동적 질의를 지원하는 것은 아니다.

다른 많은 시스템에서 간단하고 확장성 높은 모델을 위해 풍부한 쿼리 기능을 포기하는 경우가 많은데,

MongoDB의 설계 목표 중 하나는 관계형 데이터베이스상에서 매우 필수적은 쿼리 성능을 유지하는 것이다.

MongoDB에서 쿼리 언어는 아래와 같이 작동한다.

db.posts.find({'tags': 'politics', 'vote_count': {'$gt': 10}})

SQL 쿼리는 엄격하게 정규화 된 모델에 의존하는 반면, MongoDB의 쿼리는 태그가 각 도큐먼트에 포함되어 있다고 가정한다.

여러개의 속성 (tags 검색, vote_count 검색)을 조합하여 질의할 수 있는 능력을 보여주는데, 이것이 애드혹 쿼리의 본질적인 강점이다.

인덱스

MongoDB에서 인덱스는 B-Tree(B-Tree?) 로 구현되어 있다.

관계 데이터베이스에서 기본적으로 제공하는 기능인 인덱스는 범위를 스캔하거나 정렬을 하는 것과 같이 간단한 쿼리에 대해 최적화 되어 있다.

 

하지만 WiredTiger는 LSM tree(Log-Structed Merge-trees)를 지원한다고 한다.

B-Tree?

이진 트리가 자식 노드가 최대 2개인 노드를 말하는 것이라면 B-Tree는 자식 노드의 개수가 2개 이상인 트리를 말한다.

또한 노드내의 데이터가 1개 이상일 수가 있다. 만약 노드 내 최대 데이터 수가 2개라면 2차 B-Tree, 3개라면 3차 B-Tree라고 말한다.

그리고, 노드의 데이터 수가 N개라면 자식 노드의 개수는 N+1개 이다.

또한, 6보다 작은 값은 왼쪽 서브트리, 6과 17 사이의 값은 중간 서브트리, 17보다 큰 값은 오른쪽 서브트리에 들어가는 구조를 볼 수 있다.

탐색법은 작은 값은 왼쪽, 큰값은 오른쪽에 있기 때문에 root 노드부터 시작해 하향식으로 탐색해 나간다.

Web RTC는 웹, 앱 애플리케이션이 별도의 소프트웨어 없이 음성, 영상 미디어 혹은 텍스트, 파일같은 데이터를 브라우저 끼리 주고 받을 수 있게 만든 기술이다. WebRTC로 구성된 프로그램들은 별도의 플러그인이나 소프트웨어 없이 p2p 화상회의 및 데이터 공유를 한다.

→ 웹 브라우저 상에서 어떤 플러그인 없이 음성채팅은 물론이며 화상채팅, 데이터 교환까지고 가능하게 하는 기술

WebRTC 기술은 p2p 통신에 최적화 되어있다.

 

STUN

NAT 환경 클라이언트는 자기가 누구인지(내 공인 IP가 무엇인지) 모르기 때문에 라우터에 연결된 공인 IP를 알아내는 과정이 필요한데, 이를 STUN이 한다.

사설 IP를 가진 PC에서 요청을 보내면 라우터를 통해 인터넷 망으로 나온다. 이때는 라우터의 공인 IP로 변환되어 나오고ㅡ STUN 서버로 도착하면 패킷의 헤더를 읽어 IP와 포트를 테이블에 저장하는 동시에 응답으로 보냅니다. 이로써 PC는 자신이 속한 라우터의 공인 IP를 알게 되고 자신을 구별하는 포트번호를 알 수 있게 됩니다.

NAT(네트워크 주소 변환) 란

IP 패킷에 있는 출발지 및 목적지의 IP주소와 TCP/UDP 포트 숫자등을 바꿔 재기록하면서 네트워크 트래픽을 주고 받게 하는 기술

NAT를 쓰는 이유는

여러 대의 호스트가 하나의 공인 IP 주소를 사용하여 인터넷에 접속 할 수 있도록 하는데, 이런 방식을 사용하면

  • 공인 IP 주소를 절약할 수 있으며
  • 공개된 인터넷과 사설망 사이에 방화벽을 설치하여 외부 공격으로부터 사용자의 통신망을 보호할 수 있다.

시그널링 이란

RTC PeerConnection 통신에 사용할 프로토콜, 채널, 미디어 코덱 및 형식, 데이터 전송 방법, 라우팅 정보와 NAT 통과 방법을 포함한 통신 규격을 교환하기 위해 두 장치의 제어 정보를 교환하는 과정을 의미한다.

시그널링은 WebRTC 자체에서 지원하는 기능이 아니라, WebRTC 연결 전 미리 준비해야 하는 과정이다. WebRTC 자체의 스펙도 아니기 때문에, 한 가지로 딱 정해진 방법이 없다.

정해진 방법이 없는 이유는 알 수 없는 두 장치가 언제 어떤 방식으로 연결 될 수 있는 지의 모든 경우를 예측하는 것이 불가능하기 때문이다. 따라서 개발자는 자신에게 맞는 최적의 방법을 선택적으로 적용할 수 있다.

일반적으로 두 개의 장치를 연결 할 수 있는 시그널링 서버를 직접 구축하거나, 시그널링 서버를 제공해주는 외부 솔루션을 적용할 수 있다.

만약 직접 구축한다면 웹 소켓이나 서버 전송 이벤트 방법을 적용할 수 있다.

시그널링 정보를 조회할 수 있는 API를 만든 후 브라우저 단에서 주기적으로 XHR을 요청하는 폴링 기법을 쓸 수도 있다.

→ 웹 브라우저 상에서 어떤 플러그인 없이 음성채팅은 물론이며 화상채팅, 데이터 교환까지고 가능하게 하는 기술

WebRTC 기술은 p2p 통신에 최적화 되어있다.

P2P 통신이란?

P2P 연결방식

Peer to Peer 라는 뜻으로, 서버 없이 각 단말들이 서로 동등한 입장에서 통신을 하는 네트워크를 말한다.

하지만 여러명의 사용자가 접속하는 상황이 생겼다면, 접속중인 사용자 PC에게 엄청난 과부하를 줄 수 있다.

그렇기 때문에 1:N, M:N 통신에 최적화 되어있는 SFU 와 MCU를 사용한다.

SFU (Selective Forwording Unit)

SFU 연결방식

SFU는 1:N 연결에 최적화 되어 있다. 중앙 서버를 두고 클라이언트를 1명씩 중개해주는 역할을 한다.

 

💡 pull, push 할때마다 로그인하기 번거롭기 때문에, ssh agent를 설정해두자

ssh key 생성

ssh-keygen -t rsa -b 4096 -C "본인 gitlab email"
# id_rsa 이름 설정 (id_gitlab)
# 이후로 뭐라 나오는데 무시하고 엔터

ssh agent 활성화

eval "$(ssh-agent -s)"

ssh agent에 ssh key 등록

ssh-add ~/.ssh/id_gitlab

remote branch ssh 프로토콜로 지정

# 기존 remote 삭제
git remote rm origin

# ssh 프로토콜 origin 생성
git remote add origin ssh://git@git.elicer.io:2201/elice/backend/elice-api.git

Gitlab에 ssh key 등록

cat ~/.ssh/id_gitlab
# 결과 값 복사

위에서 복사한 결과 값 붙여넣기 후 Add Key

gitlab ssh keys setting

PostgreSQL에서는 동시성 제어를 위해 여러가지 모드의 lock을 제공합니다. 이런 lock에도 여러가지 종류가 있고, 명시적으로 사용되는 경우/묵시적으로 사용되는 경우가 있는데, 자세히 알아보도록 합시다.

시작하기 전 3줄요약

  1. Lock이 미치는 범위를 level로 나눈다
  2. Lock 모드별로 충돌하는 관계가 존재한다
  3. Lock은 트랜잭션 종료 시 혹은 롤백시에 풀린다.

lock이 미치는 범위를 Level로 나누는데, Table-Level Lock, Row-Level Lock, Page-Level Lock, Database-Level Lock 까지 다양하게 존재합니다.
해당 글에서는 Table-Level LockRow-Level Lock 에 대해서만 설명하겠습니다.

Table-Level Lock

Table Level Lock은 테이블 수준에 락을 거는 방법입니다. 만약 테이블 내에 100개의 로우가 있다고 하면 하나의 로우에 접근하는 동안 나머지 99개의 로우에 접근 할 수 없기 때문에, 다중 사용자 환경에서는 사용하지 않는 편이 좋습니다. *(보통 테이블 전체 로우의 변경이 있는 DDL 구문과 함께 사용됩니다. (ex) TRUNCATE, ALTER))
*

이런 Table Level Lock은 LOCK 명령어를 이용해서 명시적으로 걸어줄 수도 있지만,
우리가 특정 쿼리문을 사용할 때 마다 암묵적으로 걸리게 됩니다. 어느 상황에 어느 락이 걸리는 지는 해당 문서를 보면 알 수 있습니다.

Row-Level Lock

Row-Level Lock은 row 수준에 락을 거는 방법입니다.

SELECT ~ FOR SHARE과 같은 DML 구문과 함께 가장 자주 사용되는 Lock입니다.


만약 이런 Lock 종류가 하나밖에 없다면, 여러 종류의 트랜잭션에서 lock을 알맞게 사용하기 어려워지는데요,
이런 상황을 처리하기 위해 Lock에는 Lock모드라는 것이 존재합니다.

Lock 모드?

Lock 모드 별로 서로 충돌하는 Lock 모드가 있으며,
충돌한다면 해당 리소스(table, row)에 동시에 접근할 수 없게 됩니다.
ex)

  • ACCESS SHARE락은 ACCESS EXCLUSIVE 락과 충돌한다,
  • ROW SHARE락은 EXCLUSIVE, ACCESS EXCLUSIVE 락과 충돌한다

Table-Level Lock에는ACCESS SHARE, ROW SHARE 등,
Row-Level Lock에는 FOR UPDATE, FOR UPDATE 등 여러가지 Lock 모드가 존재합니다.
이런 모드들의 이름은 일반적으로 사용되는 경우를 나타내지만, 모드마다 기능적으로 다른 점은 없습니다.

각 모드마다 차이점은 오직 "어떤 모드의 lock과 충돌하는가" 입니다.
부연설명하자면 충돌하는 lock 모드의 종류가 다른 것이 차이점이라고 볼 수 있겠습니다.

이런 lock 모드별 충돌되는 경우는 아래의 표를 보고 확인할 수 있습니다.

Table-Level Lock Mode

Row-Level Lock Mode


Ref

Chapter 13. Concurrency Control

좀 늦은감이 있지만 1/1 ~ 3/30 까지의 회고를 해보려고 한다. 난 뭘하고 살았을까

🏫 회사

현재 재직중인 엘리스에서 이루고자 한 1분기 목적은 "내가 개발하는 서비스 플로우에 대한 완벽한 이해" 였다.

엘리스에서 내가 개발하는 서비스는 크게 3가지로 나뉜다.

  • 엘리스 과목, 수업 자료, 계정 정보를 관리하는 Elice Core
  • 외부 기관과 학습 관리 서비스 연동을 지원하는 LMS
  • 채용 서비스를 제공하는 Elice Works

이 서비스들에 대한 이해도는 순서대로 30% 50% 80% 정도라고 생각한다.
측정 기준은 "각 엔드포인트가 어떤 로직으로 동작하는지 아는가" 이다.

이전에는 복잡한 엔드포인트들은 전혀 몰랐는데

  • WebSocket 메세지 타입 모듈화 작업
  • Worker api call 비동기 작업

두 작업을 진행하면서 이해하게 되었다.

진행했던 재밌던 일은, 아래와 같다

데이터 추출 자동화

운영팀에서 주기적으로 요청하는 데이터가 있는데, 이를 파이썬 schedule 라이브러리로 자동화했다.
이거에 대해서 아파치의 airflow같은 툴 혹은 프레임워크를 만들어볼까 생각을 했는데, 바빠져서 pending되었다. 내 할일 창고에 쌓아두고 빠른 시일내에 개발해야겠다.

🚧 개발

개발쪽 목표는 딱히 없었다. OKR에는 알고리즘 공부가 적혀있었지만 전혀 진행되지 않았다. 반성한다.

알고리즘 공부를 포기하고 내 분야에 대한 생각을 좀 더 하게 되었다.
내가 백엔드를 선택한 이유는

  1. 도달할 수 있는 한계점이 높다
  2. 성과에 따른 지표가 뚜렷하다
  3. 복잡한 아키텍처를 설계하고 구현해내면 항상 짜릿하다

이에 대해서 첨언하자면 실무에서 쿼리 튜닝을 진행해본 경험이 있었는데,
약 7배 정도 성능이 향상되는 것을 보고 백엔드는 성과 지표를 뚜렷히 나타낼 수 있구나라는걸 다시한번 느끼게 되었다.

그 외에도 몇가지 활동을 했다.

질문 아카이브

모르는 기술적인 문제가 있을 때마다 사수님들을 찾아가서 물어보는 것이 팀의 생산성을 떨어뜨리는 행위라고 생각했다.
그렇기 때문에 먼저 문제을 제대로 정의하고, 최대한 스스로 해결하기 위해 나만의 질문 템플릿을 만들어야 겠다고 생각했다.

질문들을 저장하는 레포지토리를 생성해서 문제을 정의하고 찾아낸 해결법을 정리해 뒀다.

https://github.com/hanbin8269/q-archive

알고리즘 문제 로드맵

쌓아놓고 풀기위해 기초 문제들을 노션에 리스트업 해두었다.
언젠간 풀지않을까..? 👀

📝 블로그

블로그는 총 3개를 쓰려고 했는데 의지박약으로 1개만 포스팅했다 ㅎㅎ

RabbitMQ 관련 포스팅

개인 프로젝트 진행하며 docker-compose 로 rabbitMQ를 띄우게 되었는데 알아놓으면 좋을거 같은 설정들을 정리하고 싶어 작성한 포스팅이다

rabbitMQ 설정 관련된 내용 외에도 docker volume 마운트 관련된 간단한 내용을 정리했다.

💪 운동

운동 측정기준은 매일 팔굽혀펴기 30개씩 완료 했는가로 매우 명확했다.

측정 해보니 약속있는 날에 빼먹어서 90% 정도 완료했다. 근데 의미 없는것 같음...
이사하면 헬스장 끊어서 제대로 다녀야겠다. 2분기는 운동 안할래


정리

막상 회고를 하고 나니 1분기에 한게 너무 없었다.
다음 2분기에는 아래 내용을 좀 더 잘 지켜보려고 한다.

  • 명확한 측정기준
  • OKR을 계속 상기시키기 위한 수단
  • 유연한 OKR 변경

다음 분기에는 내가 더 나은 사람이 되어있으면 좋겠다 🙏

+ Recent posts