0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

effect-tsでCacheを利用してDBの値に有効期限を適用

Posted at

前回作成したソースコードにCacheを適用して、有効期限以内ならキャッシュデータを返し、そうでないなら新しい値を取得するようにした

参考

改良版

キャッシュされたかどうかは判別可能な気がする
有効期限がきれたかどうかは判別不可能な気がする

import { Cache, Duration, Effect, Layer } from "effect"

type type_employee_id = string
type type_department_id = string

// ---- db : employee ----

interface interface_employee {
  employee_id: type_employee_id
  employee_name: string
  department_id: type_department_id
}

class database_employee_service extends Effect.Service<database_employee_service>()("database_employee_service", {
  succeed: ({
    get_one_employee: (employee_id: type_employee_id) =>
      Effect.gen(function*() {
        yield* Effect.log("get_one_employee start")
        yield* Effect.sleep(Duration.seconds(5))
        const data: interface_employee = {
          employee_id,
          employee_name: "test employee",
          department_id: test_department_id
        }
        yield* Effect.log("get_one_employee end")
        return data
      })
  })
}) {}

const employee_cache = Cache.make({
  capacity: 100,
  timeToLive: Duration.seconds(5),
  lookup: (employee_id: type_employee_id) =>
    Effect.gen(function*() {
      const DatabaseEmployee = yield* database_employee_service
      const employee = yield* DatabaseEmployee.get_one_employee(employee_id)
      return employee
    })
})

// ---- db : department ----

interface interface_department {
  department_id: type_department_id
  department_name: string
}

class database_department_service extends Effect.Service<database_department_service>()("database_department_service", {
  succeed: ({
    get_one_department: (department_id: type_department_id) =>
      Effect.gen(function*() {
        yield* Effect.log("get_one_department start")
        yield* Effect.sleep(Duration.seconds(3))
        const data: interface_department = {
          department_id,
          department_name: "test department"
        }
        yield* Effect.log("get_one_department end")
        return data
      })
  })
}) {}

const department_cache = Cache.make({
  capacity: 100,
  timeToLive: Duration.seconds(5),
  lookup: (department_id: type_department_id) =>
    Effect.gen(function*() {
      const DatabaseDepartment = yield* database_department_service
      const department = yield* DatabaseDepartment.get_one_department(department_id)
      return department
    })
})

// ---- test data ----

const test_employee_id: type_employee_id = "e_00_test"
const test_department_id: type_department_id = "d_00_test"

// ---- layer -----------------

const service_live = Layer.mergeAll(database_employee_service.Default, database_department_service.Default)

interface employee_data {
  employee: interface_employee
  department: interface_department
}

class domain_employee_service extends Effect.Service<domain_employee_service>()("domain_employee_service", {
  effect: Effect.gen(function*() {
    const CacheEmployee = yield* employee_cache
    const CacheDepartment = yield* department_cache
    return {
      get_one_employee_data: (
        employee_id: type_employee_id
      ) =>
        Effect.gen(function*() {
          const isHitEmployee = yield* CacheEmployee.contains(employee_id)
          if (isHitEmployee) yield* Effect.log("employee_data from cache")
          const employee = yield* CacheEmployee.get(employee_id)

          const isHitDepartment = yield* CacheDepartment.contains(employee.department_id)
          if (isHitDepartment) yield* Effect.log("department_data from cache")
          const department = yield* CacheDepartment.get(employee.department_id)
          const result: employee_data = {
            employee,
            department
          }
          return result
        })
    }
  }),
  dependencies: [service_live]
}) {}

const get_data_from_domain = Effect.gen(function*() {
  const DomainEmployee = yield* domain_employee_service
  const result_employee = yield* DomainEmployee.get_one_employee_data(test_employee_id)
  yield* Effect.log(result_employee)
  const result_employee_2 = yield* DomainEmployee.get_one_employee_data(test_employee_id)
  yield* Effect.log(result_employee_2)
  yield* Effect.sleep(Duration.seconds(5))
  const result_employee_3 = yield* DomainEmployee.get_one_employee_data(test_employee_id)
  yield* Effect.log(result_employee_3)
})

const domain_live = Layer.mergeAll(
  domain_employee_service.Default
)

const program_domain = Effect.provide(get_data_from_domain, domain_live)

// ---- Entry Point ----
Effect.runPromise(program_domain)

実行結果

timestamp=2025-09-14T15:24:50.368Z level=INFO fiber=#0 message="get_one_employee start"
timestamp=2025-09-14T15:24:55.378Z level=INFO fiber=#0 message="get_one_employee end"
timestamp=2025-09-14T15:24:55.387Z level=INFO fiber=#0 message="get_one_department start"
timestamp=2025-09-14T15:24:58.394Z level=INFO fiber=#0 message="get_one_department end"
timestamp=2025-09-14T15:24:58.397Z level=INFO fiber=#0 message="{
  \"employee\": {
    \"employee_id\": \"e_00_test\",
    \"employee_name\": \"test employee\",
    \"department_id\": \"d_00_test\"
  },
  \"department\": {
    \"department_id\": \"d_00_test\",
    \"department_name\": \"test department\"
  }
}"
timestamp=2025-09-14T15:24:58.400Z level=INFO fiber=#0 message="employee_data from cache"
timestamp=2025-09-14T15:24:58.403Z level=INFO fiber=#0 message="department_data from cache"
timestamp=2025-09-14T15:24:58.406Z level=INFO fiber=#0 message="{
  \"employee\": {
    \"employee_id\": \"e_00_test\",
    \"employee_name\": \"test employee\",
    \"department_id\": \"d_00_test\"
  },
  \"department\": {
    \"department_id\": \"d_00_test\",
    \"department_name\": \"test department\"
  }
}"
timestamp=2025-09-14T15:25:03.413Z level=INFO fiber=#0 message="employee_data from cache"
timestamp=2025-09-14T15:25:03.416Z level=INFO fiber=#0 message="get_one_employee start"
timestamp=2025-09-14T15:25:08.430Z level=INFO fiber=#0 message="get_one_employee end"
timestamp=2025-09-14T15:25:08.431Z level=INFO fiber=#0 message="department_data from cache"
timestamp=2025-09-14T15:25:08.432Z level=INFO fiber=#0 message="get_one_department start"
timestamp=2025-09-14T15:25:11.442Z level=INFO fiber=#0 message="get_one_department end"
timestamp=2025-09-14T15:25:11.445Z level=INFO fiber=#0 message="{
  \"employee\": {
    \"employee_id\": \"e_00_test\",
    \"employee_name\": \"test employee\",
    \"department_id\": \"d_00_test\"
  },
  \"department\": {
    \"department_id\": \"d_00_test\",
    \"department_name\": \"test department\"
  }
}"
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?