반응형
1. links vs depends_on:
links와 depends_on 둘 다 dependency와 container의 생성순서를 정해준다.
하지만 links는 위의 기능과 link된 container의 이름을 내부코드에서 사용하여 접근할 수 있다.
예시:
web:
  image: example/my_web_app:latest
  links:
    - db
    - cache

db:
  image: postgres:latest

cache:
  image: redis:latest
위의 예시에서 links를 사용한다면 web 내부에서 db:5432 로 접근할 수 있지만 depends_on을 사용한다면 접근할 수 없다.

https://stackoverflow.com/questions/35832095/difference-between-links-and-depends-on-in-docker-compose-yml

또, links와 depends_on은 생성순서는 지정해주지만, 먼저 시작된 컨테이너가 실제로 '사용가능'한 상태인지는 확인하지 않는다고한다. 예를 들자면 데이터베이스 컨테이너를 먼저 시작은 하지만 이 데이터베이스 컨테이너가 현재 연결가능하지 않을수도 있다는 것이다.

2. Volumes:
Volume의 역할인 로컬에 있는 파일을 도커 컨테이너로 마운트하는 역할을 한다. 크게 2가지로 나눌 수 있다.
    - 그냥 마운트: 그냥 그대로 파일/디렉토리를 복사하여 넣는 것이다.
       (예: temp_volume: /app/static/ <- 로컬의 /app/static/을 마운트하고 temp_volume으로 명명)
    - 연결: 데이터베이스가 실행중인 컨테이너를 종료와 함께 삭제하면, 컨테이너가 실행된 시간동안 저장된 데이터가 모두 날아가게 된다. 이를 방지하기 위해 로컬의 폴더와 연결하면 컨테이너의 정보가 날아가더라도 남아있게 된다.
       (예: ./docker/data:/var/lib/mysql/data <- 컨테이너의 /var/lib/mysql/data를 로컬의 ./docker/data와 연결)










반응형
블로그 이미지

개발자_무형

,
반응형
Viewset을 사용하면 관련된 view들을 하나의 클래스로 묶을 수 있다.

class UserViewSet(viewsets.ViewSet):
    """
    A simple ViewSet for listing or retrieving users.
    """
    def list(self, request):
        queryset = User.objects.all()
        serializer = UserSerializer(queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        queryset = User.objects.all()
        user = get_object_or_404(queryset, pk=pk)
        serializer = UserSerializer(user)
        return Response(serializer.data)
from rest_framework import routers

router = routers.SimpleRouter()
router.register(r'users', UserViewSet)
router.register(r'accounts', AccountViewSet)
urlpatterns = router.urls
위의 예시는 아래의 URL 패턴을 만든다
  • URL pattern: ^users/$ Name: 'user-list'
  • URL pattern: ^users/{pk}/$ Name: 'user-detail'
  • URL pattern: ^accounts/$ Name: 'account-list'
  • URL pattern: ^accounts/{pk}/$ Name: 'account-detail'

Viewset에서 사용하는 self.action은 router가 자동으로 변환한 것이다.
URL StyleHTTP MethodActionURL Name
{prefix}/GETlist{basename}-list
POSTcreate
{prefix}/{url_path}/GET, or as specified by `methods` argument`@action(detail=False)` decorated method{basename}-{url_name}
{prefix}/{lookup}/GETretrieve{basename}-detail
PUTupdate
PATCHpartial_update
DELETEdestroy
{prefix}/{lookup}/{url_path}/GET, or as specified by `methods` argument`@action(detail=True)` decorated method{basename}-{url_name}
위에 표는 URL Style + HTTP Method가 어떻게 Action으로 변환되는지를 보여준다.
만약 POST users/라고 쿼리를 보내면 {prefix}/ + POST로 action은 list가 된다.
만약 GET users/123라고 쿼리를 보내면 {prefix}/{lookup}/ + GET으로 action은 retrieve가 된다.

perform_create()는 create()가 호출된 과정에서 serializer를 사용하여 serializer.save()를 하기 위해 호출된다.
perform_create()와 create()를 구분해서 사용하는법
추가1
추가2

serializer.save()는 객체가 이미 존재하면 update를, 없다면 create를 해준다





반응형
블로그 이미지

개발자_무형

,
반응형
Django REST Framework의 ModelViewSet과 create() 관련 찾아보고 공부한 내용을 정리해보려고 한다.

기본적으로 ModelViewSet은 GenericAPIView라는 클래스를 상속받기 때문에 .list(), .create()  .list(), .create() 등을 기본으로 내장하고 있다. 따라서 이 중에서 기본 내장되어 있는 메소드 중 동작을 변경하고 싶은 것들만 overriding해서 수정하면 된다.

그리고 ModelViewSet은 GenericAPIView를 상속받고 있기 때문에 최소한 queryset와 serializer_class는 세팅해주어야 한다.

from rest_framework import status
from rest_framework.response import Response


def create(self, request, *args, **kwargs):
    serializer = self.get_serializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    self.perform_create(serializer)
    headers = self.get_success_headers(serializer.data)
    return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

def perform_create(self, serializer):
    serializer.save()
위의 코드를 보면 이해가 조금 더 쉬울 것 같다. perform_create()은 create()의 동작 중 일부분을 overriding한다고 생각하면 되는데, serializer.save()가 호출될 때 perform_create()가 호출된다고 생각하면 된다. 위의 경우 명시적으로 perform_create()를 호출했지만, django에서는 개발자의 짐을 덜어주기위해 mixin으로 앞에서 설명한 list(), create()등을 제공하는 데, 이때 자동으로 save()대신 perform_create()를 호출하는 것이다. 

만약 perform_create()가 없었다면, create()의 전체적인 로직은 변경할 필요가 없어 작성하지는 않고 mixin이 기본 제공하는 메소드를 사용할 때,  save()가 될 때의 행동을 부분적으로 재정의를 하고 싶더라도 create()전체를 재작성해야될 것이다.



반응형
블로그 이미지

개발자_무형

,
반응형

Scope란 connection의 정보를 뜻한다. remote IP, username, lifetime of connection등의 정보를 가지고 있다. Application은 한번의 scope당 한번씩 instantiate된다. HTTP에서는 각 request마다, socket에서는 각 WebSocket connection마다이다.

Consumer

각각의 프로토콜은 각자 다른 event들이 발생하며 이 event들은 메소드로 표현된다. 개발자는 각각의 event들을 어떻게 처리할지 코드만 작성하면 Django의 Channel이 스케쥴링과 병렬적으로 실행시켜준다.

 

 

class LogConsumer(WebsocketConsumer): def connect(self, message): Log.objects.create( type="connected", client=self.scope["client"], )

Channel은 asynchronous event loop을 기반으로 돌아간다.  하지만 위처럼 코드를 작성하면 synchronous thread에서 실행이 된다. 따라서 위의 코드처럼 blocking operation도 문제없이 잘 작동한다.

class PingConsumer(AsyncConsumer): async def websocket_connect(self, message): await self.send({ "type": "websocket.accept", }) async def websocket_receive(self, message): await asyncio.sleep(1) await self.send({ "type": "websocket.send", "text": "pong", })

위처럼 작성하면 asynchronous하게 작동한다.

 

 

Cross-Process Communication

Application내에서 통신하는 것은 각 application바다 존재하는 application instance를 통해서 하면 되지만, application끼리 통신하는 것은 이야기가 달라진다. 데이터베이스 polling을 통해서 할 수도 있지만 Channels는 channel layer라는 개념을 도입했다. 각각의 application instance는 unique channel name을 가지고 있으며, group에 join할 수 있다.

 

# In a consumer self.channel_layer.send( "myproject.thumbnail_notifications", { "type": "thumbnail.generate", "id": 90902949, }, )

위의 코드처럼 다른 process에게 broadcasting해줄 수 있다.

 

 

 

반응형
블로그 이미지

개발자_무형

,
반응형

Consumer하는 일 중 몇가지:

    - 이벤트가 발생했을 때 사용할 함수를 작성하기만 하면 된다. event loop전체를 다 쓸 필요가 없다.

    - synchronous / asynchronous 코드를 작성하기만 하면 threading을 포함한 나머지를 다 처리해준다.

 

Consumer Events

from channels.consumer import SyncConsumer class EchoConsumer(SyncConsumer): def websocket_connect(self, event): self.send({ "type": "websocket.accept", }) def websocket_receive(self, event): self.send({ "type": "websocket.send", "text": event["text"], })

 

Consumer는 type과 매칭되는 몇가지의 메소드들로 구성되어 있다. type에서 .를 _로 바꾼것이 메소드 이름이다. 즉 위의 예시에서

"type": "websocket.accpet" 는 websocket_accept()를 호출하고

"type": "websocket.send"는 websocket_send()를 호출한다.

 

그렇다면 위의 예시에서 websocket_receive(self, event)는 event["text"]를 접근하고 있는데 websocket_receive의 event안에 text가 있다는 것은 어떻게 알았을까? 이는 ASGI에서 정해진 규약을 따른다.

 

AsyncConsumer vs SyncConsumer

AsyncConsumer와 SyncConsumer의 사용처에 대해서는 쉽게 정리하자면 asynchronous 코드들만 실행될 경우에는 AsyncConsumer를, ORM처럼 Synchronous 코드는 SyncConsumer에서 호출하도록 한다.

 

 

Closing Consumers

Socket이나 Connection이 끊기면 보통은 서버에 event(http.disconnect or websocket.disconnect)가 보내진다. disconnect에 관련된 작업을 마치면 channels.exceptions.StopConsumer를 발생시켜 ASGI application을 정상적으로 중지시켜야된다. 따로 중지시키지 않는다면, server는 timeout시간이 되면 자동종료시키고 warning을 띄운다.

 

 

Channel Layers

from channels.consumer import SyncConsumer class EchoConsumer(SyncConsumer): channel_layer_alias = "echo_alias"

위의 코드처럼 channel_layer_alias를 별도로 명시해주지 않는다면 Consumer는 default channel layer를 사용한다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형
블로그 이미지

개발자_무형

,
반응형
원문: https://www.qwertee.io/blog/postgresql-b-tree-index-explained-part-1/
원문: https://en.wikipedia.org/wiki/B-tree

Index의 목적은 적은 양의 데이터를 찾을때 disk I/O를 줄이는 것이다. disk를 검색하는 작업은 O(n)이지만 index가 유용하기 위해서는 그보다 더 빨라야한다.

 

PostgreSQL의 default index는 B-Tree로 구성되며 좀 더 정확하게는 B+ Tree로 구성된다. B-Tree와 B+ Tree의 차이점은 key가 저장되는 방식에 있다. B-Tree에서는 각각의 Key들은 leaf node이든 non-leaf node이든 딱 1번만 저장되지만 B+ Tree는 모든 Key들은 leaf node에 존재하고 non-leaf node에도 존재할 수 있다. 

 

 

 

 

Leaf Nodes

Leaf Node는 table row을 가리키는 포인터를 가지고 있는 ROWIDkey를 가지고 있다. 각각의 leaf node사이에는 index key에 기반한 순서가 있다. 이 순서는 실제로 저장되는 physical order와는 독립적이다. physical order와 독립적이기 때문에 INSERT같은 작업을 하더라도 실제로 data를 옮기지 않고 몇개의 Pointer만을 업데이트하면 되기 때문에 작업을 빠르게 수행할 수 있다. 이렇게 logical order를 따라 저장되어 있기 때문에 오름차순과 내림차순 검색이 가능하다.

 

Internal Nodes(Non-leaf Nodes)

위의 사진을 보면 좀 더 쉽게 이해가 될 것 같은데 internal node에는 pre-define된 범위가 있다. 그리고 포인터들은 각각의 범위안의 노드들을 가리킨다. 즉 위의 예시에서 7보다 작은 key들이 모여져 있는 노드들을 가리키는 포인터, 7~16을 가리키는 포인터 16이상을 가리키는 포인터들이 있다.

 

보통 한묶음(노드)당 d~2d개의 key들로 구성이된다(d가 최소개수). 만약 2d개의 key들이 묶여있는데 한개가 추가된다면 d개로 이루어진 2묶음을 만들고, 2d+1개의 key들 중 중간값 1개를 부모 노드의 중간으로 올린다.

 

하나의 노드에서 파생되는 branch의 개수는 해당 노드에 포함된 key의 개수+1개이다.

 

B-Tree Search

위의 예시는 key 8을 찾는 예시이다.

 

1. 8은 root의 모든 key들보다 크므로 마지막 포인터를 타고 내려간다.

2. 첫번째 internal node(8)와 값이 같으므로 첫번째 포인터를 타고 내려간다.

3. 두번째 internal node(6)보다 값이 크므로 마지막 포인터를 타고 내려간다.

4. 이제 leaf node에 도달했기때문에 해당 leaf node부터 쭉 오른쪽으로 타고 가면서 찾고자 하는 값보다 큰 값이 나올때까지 검색한다.

 

 

 

 

 

 

반응형
블로그 이미지

개발자_무형

,
반응형

Database page:

Database에서 page는 DB가 data를 저장/관리하는 가장 기본적인 단위이다.

쉽게 비유하자면, 

Database
Page Page
Row Sentence

하지만 (DB마다 조금씩 다르겠지만), row는 page 용량의 반을 넘어갈 수 없다.

https://stackoverflow.com/questions/4401910/mysql-what-is-a-page

 

Clustered Index vs Non-clustered Index:

Clustered Index는 index에 저장되는 순서와 같은 순서로 disk에 저장되는 것이다. 따라서 clustered index는 1종류밖에 없다.

Non-clustered Index는 실제 물리적으로 저장되는 것과는 별도로 다른 리스트가 존재하는 것이다. 이 리스트는 물리적인 주소값을 저장하고 있는 포인터를 가지고 있다.

만약 모든 column을 반환하고 싶다면 주로 clustered index가 더 빠르다. 이유는 Non-clustered Index는 인덱스에서 테이블을 한번 더 찾아가야 되기 때문이다.

https://stackoverflow.com/questions/1251636/what-do-clustered-and-non-clustered-index-actually-mean

 

어떤 Column에 Index를 생성해야할까?

Primary Key를 가지는 새로운 테이블을 생성하게되면 clustered index가 자동으로 primary key의 column에 대해 생성되게 된다. 대부분의 경우에는 괜찮지만 가끔가다 가장 효과적이지 않은 경우도 있다.

 

Clustered Index로 사용되는 column은 unique하고 새로운 entry가 들어갈 때마다 값이 증가되는 column인것이 좋다. 그렇지 않을 경우 새로운 row가 insert될 때마다 index는 새로 정렬되어야 한다. 새로 입력된 row가 있어야될 위치를 찾기위해 data page를 새로 분리하고 위치를 옮기는 작업이 필요하다.

 

- 같은 맥락으로 자주 값이 변경되는 column도 Clustered Index로 사용되면 안된다. 매 업데이트때마다 재정렬을 해야되기 때문이다.

- 큰 데이터를 저장하는 column(BLOB, GUID)를 index를 사용하는 것은 sort할 때 비효율적이다.

 

https://www.sqlshack.com/poor-database-indexing-sql-query-performance-killer-recommendations/

 

 

 

 

 

반응형
블로그 이미지

개발자_무형

,
반응형
https://leopard.in.ua/2015/04/13/postgresql-indexes#.XfeTyugzZaR

Index는 추가적인 자료구조로서 아래의 목적들의 달성을 위해 도움이 된다:

1. 데이터 검색

2. Optimizer

3. JOIN

4. Relation: except/intersect를 위해 사용될 수 있다.

5. Aggregation: COUNT/MIN/MAX 등의 Aggregatation function들을 효율적으로 계산할 수 있게 해준다.

6. Grouping

 

Index Types: PostgreSQL에는 여러가지 Index타입들이 있으며 각각 다른 사용법을 가지고 있다.

 

B-Tree index: B-Tree는 CREATE INDEX를 하면 default로 생성되는 index이다. B-Tree의 B는 Balanced의 줄임말로서 트리의 양쪽이 거의 같은 양의 데이터를 가지도록 하는 것이 기본개념이다. B-Tree는 self-balancing 트리로서 sorted data를 유지하고, 검색, 순차적인 접근, 삽입, 삭제를 log시간안에 할 수 있는 자료구조이다. B-Tree는 큰 양의 데이터를 읽고 써야되는 경우에 용이하다.

https://en.wikipedia.org/wiki/B-tree

 

R-Tree index: R-Tree는 Rectangle-Tree의 줄임말이다. R-Tree는 주로 공간정보를 저장할 때 사용되는 트리다. 예를 들자면 지리좌표나, 다각형 좌표 등이다. R-Tree의 핵심 아이디어는 가까이 있는 객체들을 그들의 minimum bounding rectangle로 묶어 한단계 상위 계급인 트리로 표현하는 것이다. 모든 객체들은 이런 bounding rectangle에 포함되어 있고, 이 bounding rectangle과 intersect하지 않는 쿼리는 이 rectangle에 포함된 모든 객체들과도 intersect하지 않는다. leaf 레벨에서는 rectangle을 각각의 객체로 보고, 상위 레벨에서는 rectangle을 object들의 모음으로 본다.

 

https://en.wikipedia.org/wiki/R-tree
https://en.wikipedia.org/wiki/Minimum_bounding_rectangle

 

More:

Hash index

Bitmap index

GiST index

GIN index

반응형
블로그 이미지

개발자_무형

,