코딩, 개발에 대한 기록 저장소
레이블이 Database인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Database인 게시물을 표시합니다. 모든 게시물 표시

python apscheduler 의 job 관리(Job Store)

Job Store

apscheduler에서 Job Store는 스케줄러가 관리하는 작업(job)에 대한 모든 정보를 저장하고 유지하는 역할을 합니다. 여기에는 다음과 같은 정보가 포함됩니다.

  • 실행할 함수 또는 콜러블 객체: 어떤 작업을 실행해야 하는지에 대한 정보
  • 트리거 (Trigger): 작업이 언제, 얼마나 자주 실행되어야 하는지에 대한 스케줄링 규칙
  • 작업의 상태: 작업이 일시 중지되었는지, 다음 실행 시간은 언제인지 등의 상태 정보
  • 작업의 설정: 작업 ID, 실행 시 동시 실행 허용 여부 등 추가적인 설정 정보

기본적으로 apscheduler는 MemoryJobStore라는 메모리 기반의 Job Store를 사용합니다. 이 저장소는 애플리케이션이 실행되는 동안에만 작업 정보를 유지합니다.

  • 장점: 설정이 간단하고 빠릅니다. 별도의 데이터베이스 설치나 설정이 필요하지 않습니다.
  • 단점: 애플리케이션이 종료되면 모든 작업 정보가 사라집니다. 따라서 재시작 후에는 이전에 예약된 작업들이 유지되지 않습니다.

영구 저장소의 필요성

애플리케이션이 재시작되거나 여러 인스턴스(예: 웹 애플리케이션의 여러 서버)에서 스케줄러를 공유해야 하는 경우에는 메모리 기반의 저장소만으로는 충분하지 않습니다. 이때 영구 저장소를 사용하여 작업 정보를 데이터베이스와 같은 외부 저장소에 저장해야 합니다. 영구 저장소를 사용하면 다음과 같은 이점을 얻을 수 있습니다.

  • 애플리케이션 재시작 후에도 작업 유지: 애플리케이션이 종료되었다가 다시 시작되더라도 이전에 예약된 작업들이 그대로 유지됩니다.
  • 스케줄러 상태 공유: 여러 프로세스 또는 서버에서 동일한 데이터베이스를 공유하여 스케줄러 상태를 동기화하고, 작업을 중복 실행하는 것을 방지할 수 있습니다. (이를 위해서는 잠금(locking) 설정이 필요할 수 있습니다.)

주요 영구 저장소

apscheduler는 다양한 데이터베이스 백엔드를 지원하는 여러 영구 저장소를 제공합니다.

SQLAlchemyJobStore

SQLAlchemy ORM을 사용하여 다양한 SQL 데이터베이스 (SQLite, PostgreSQL, MySQL, Oracle 등)에 작업 정보를 저장합니다. 가장 널리 사용되고 강력한 옵션 중 하나입니다.

from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore

jobstores = {
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite') # SQLite 데이터베이스 사용 예시
}

scheduler = BackgroundScheduler(jobstores=jobstores)

MongoDBJobStore

MongoDB NoSQL 데이터베이스에 작업 정보를 저장합니다.

from apscheduler.jobstores.mongodb import MongoDBJobStore
from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')
jobstores = {
    'default': MongoDBJobStore(database='apscheduler', client=client)
}

scheduler = BackgroundScheduler(jobstores=jobstores)

RedisJobStore

Redis 인-메모리 데이터 구조 저장소에 작업 정보를 저장합니다. 고성능 스케줄링에 적합할 수 있습니다.

from apscheduler.jobstores.redis import RedisJobStore
import redis

redis_client = redis.Redis(host='localhost', port=6379, db=0)
jobstores = {
    'default': RedisJobStore(client=redis_client)
}

scheduler = BackgroundScheduler(jobstores=jobstores)

저장소를 선택 고려사항

  • 간단한 테스트 또는 애플리케이션 수명이 짧은 경우: MemoryJobStore로 충분할 수 있음
  • 애플리케이션 재시작 후에도 작업을 유지해야 하는 경우: SQLAlchemyJobStore, MongoDBJobStore, RedisJobStore 중 하나를 선택
  • SQL 데이터베이스를 이미 사용하고 있거나 관계형 데이터 모델이 적합한 경우: SQLAlchemyJobStore
  • NoSQL 데이터베이스를 선호하거나 확장성이 중요한 경우: MongoDBJobStore
  • 높은 성능과 빠른 읽기/쓰기가 필요한 경우: RedisJobStore. 다만, Redis는 기본적으로 휘발성 데이터베이스이므로 영구 저장을 위한 별도의 설정을 고려해야 함


SequenceField 와 ObjectIdField 의 성능 차이

SequenceField 와 ObjectIdField 의 성능 차이

SequenceField와 ObjectIdField 간에는 몇 가지 성능 차이가 있을 수 있음
성능 비교는 사용 사례에 따라 다를 수 있으며, 어떤 측면에서 성능이 더 중요한지에 따라 선택해야함

Insert 성능:

  • SequenceField: 샤딩되지 않은 환경에서는 쓰기 연산이 일반적으로 빠름. 그러나 findOneAndUpdate 연산을 사용하여 일련번호를 증가시키는 추가적인 작업이 필요할 수 있음
  • ObjectIdField: 기본적으로 샤딩에 대한 자연스러운 키로 사용되기 때문에 쓰기 성능이 우수. ObjectId는 무작위로 생성되며, 이는 여러 서버에서의 동시 쓰기 작업을 허용

일련번호 유지 및 캐싱:

  • SequenceField: 캐싱을 사용하여 일련번호를 최적화하며, 중복을 방지하기 위해 findOneAndUpdate를 사용
  • ObjectIdField: MongoDB는 ObjectId를 자동으로 생성하고 유지하며, 중복을 방지하기 위해 고유성을 보장

검색 및 쿼리 성능:

  • SequenceField: 증가하는 일련번호를 사용하면 시간순으로 정렬된 인덱스를 이용해 쿼리 성능이 향상될 수 있음
  • ObjectIdField: ObjectId는 내부적으로 시간 정보가 포함되어 있어, 시간 기반의 쿼리에 적합

고유 식별자 유형:

  • SequenceField: 사용자 정의 가능한 일련번호를 사용하므로 특별한 의미나 시간 정보가 없을 수 있음
  • ObjectIdField: ObjectId는 생성 시간과 머신 ID 등의 정보를 포함하므로 해당 정보를 활용할 수 있음

결론

성능 측면에서 ObjectIdField는 일반적으로 빠르게 동작하며, 더 많은 내장 기능을 제공
그러나 사용 사례에 따라 고유한 일련번호가 필요하거나 특별한 의미가 있는 경우 SequenceField를 사용



기본적인 DB 연결 예제에서 주의점 3가지 찾기

기본적인 DB 연결 예제

아래는 nodeJS에서 mysql 모듈을 사용하여 my_database 데이터베이스의 my_table 테이블에서 모든 데이터를 가져오는 예제입니다.

아주 기본적인 database 연결 예제입니다.
예제는 nodeJS지만 얘기하려는 내용은 대부분의 프로그래밍 언어에 포함됩니다.

const mysql = require('mysql');

const connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'password',
  database: 'my_database'
});

connection.connect();

connection.query('SELECT * FROM my_table', (error, results, fields) => {
  if (error) throw error;
  console.log('The result is: ', results);
});

실무에 적용한다고 했을 때 이 간단한 예제 안에서 주의해야 할 사항이 3가지 있습니다.
.
.
아시겠나요??
.
.
어느정도 경험 많은 개발자분들은 느낌이 딱 오시겠지만 경험이 적은 개발자분들은 인터넷에서 찾은 예제코드를 그대로 사용하는 경우가 있기 때문에 그 주위사항을 정리하였습니다.

하드코딩된 연결 정보

호스트명, 계정 정보, 데이터베이스명 등이 코드에 직접 하드코딩 되어 있습니다.
이러한 정보는 코드를 Git 같은 소스 코드 관리 시스템에 저장할 때 같이 저장되므로, 누군가 이 저장소를 복제하면 연결 정보를 쉽게 얻을 수 있습니다.
보통 setting파일을 따로 만들어 소스 코드 관린 시스템에 저장이 되지 않도록 ignore 처리를 해놓습니다.

DB root 계정 사용

DB에 연결할 때 root 계정을 사용하고 있습니다. root 사용하면 참 편하죠. 하지만 DB에 접근할 수 있는 모든 권한을 가지기 때문에 유출되면 돌이킬 수 없게 됩니다.
최소한의 권한만 가진 사용자를 생성하여 DB에 접근하도록 설정하는 것이 좋습니다.

DB 연결 종료 코드 누락

DB 연결을 종료하는 코드가 없습니다.
생성된 connectioni의 사용이 완료되면 connection.end()를 호출하여 자원을 돌려줘야 합니다.
그렇지 않으면 DB와 연결이 지속되어 불필요한 자원을 소비하고 나중에는 서버 자원 부족으로 장애가 발생할 수 있습니다.

기타

위 3가지는 정말 중요하다 생각되는 내용이고 아래 내용은 중요할 수도 있고 중요하지 않을 수도 있어서 기타로 뺐습니다.

DB 연결에 대한 예외처리

DB를 사용한다는 얘기는 내부망이든 외부망이든 네트워크로 묶여 있다는 얘기이고, 네트워크는 언제든 끊길 수 있습니다. 따라서 예외처리를 하는 것이 좋습니다.

예)

try {
  connection.connect();
  console.log('Connected!');
} catch (err) {
  console.error('Error connecting to database:', err);
}