Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

103 строки
5.4 KiB

  1. -- 01-partition.sql
  2. -- First-run initialization for new-api's partitioned logs table.
  3. -- Executed by the postgres official entrypoint against the database named by
  4. -- POSTGRES_DB (new-api), as a superuser.
  5. --
  6. -- This script is the SINGLE source of truth for the logs partitioning setup.
  7. -- The new-api Go application does NOT create or maintain partitions; it only
  8. -- skips GORM AutoMigrate for the logs table on PostgreSQL (see
  9. -- model/main.go::ensureLogTable). pg_partman owns partition lifecycle, pg_cron
  10. -- drives periodic maintenance.
  11. -- 1. Extensions --------------------------------------------------------------
  12. -- Install pg_partman into a dedicated schema named "partman" so its functions
  13. -- are referenced as partman.xxx (pg_partman 5.x does not create the schema
  14. -- automatically; without this CREATE EXTENSION lands in public and
  15. -- partman.create_parent fails with "schema partman does not exist").
  16. CREATE SCHEMA IF NOT EXISTS partman;
  17. CREATE EXTENSION IF NOT EXISTS pg_partman WITH SCHEMA partman;
  18. -- pg_cron must be created in the database set by cron.database_name (new-api),
  19. -- which requires shared_preload_libraries='pg_cron' (configured in the image).
  20. -- Include partman in the default search_path so its functions resolve without
  21. -- schema-qualifying every call below.
  22. SET search_path = partman, public;
  23. CREATE EXTENSION IF NOT EXISTS pg_cron;
  24. -- 2. Partitioned parent table -----------------------------------------------
  25. -- Columns mirror model.Log exactly. PG requires the partition key (created_at)
  26. -- to be part of the primary key, so it is a composite (id, created_at). The app
  27. -- never queries logs by id alone, so this does not affect business logic.
  28. CREATE TABLE IF NOT EXISTS public.logs (
  29. id BIGSERIAL,
  30. user_id INTEGER,
  31. created_at BIGINT NOT NULL,
  32. type INTEGER,
  33. content TEXT,
  34. username VARCHAR(64) DEFAULT '',
  35. token_name VARCHAR(255) DEFAULT '',
  36. model_name VARCHAR(255) DEFAULT '',
  37. quota INTEGER DEFAULT 0,
  38. prompt_tokens INTEGER DEFAULT 0,
  39. completion_tokens INTEGER DEFAULT 0,
  40. use_time INTEGER DEFAULT 0,
  41. is_stream BOOLEAN,
  42. channel_id INTEGER,
  43. token_id INTEGER DEFAULT 0,
  44. "group" VARCHAR(255),
  45. ip VARCHAR(64) DEFAULT '',
  46. request_id VARCHAR(64) DEFAULT '',
  47. chat_id VARCHAR(128) DEFAULT '',
  48. upstream_id VARCHAR(128) DEFAULT '',
  49. other TEXT,
  50. PRIMARY KEY (id, created_at)
  51. ) PARTITION BY RANGE (created_at);
  52. -- 3. Hand off to pg_partman --------------------------------------------------
  53. -- Weekly native range partitioning on the bigint epoch (seconds) column.
  54. -- p_epoch='seconds' -> created_at is a unix-seconds bigint
  55. -- p_type='range' -> pg_partman 5.x uses PG-native partitioning with
  56. -- p_type values 'range'/'list' (the old 'native'
  57. -- alias from 4.x was removed)
  58. -- p_interval='1 week' -> one partition per week (pg_partman 5.x dropped
  59. -- the 'weekly' preset in favor of native PG
  60. -- interval values)
  61. -- p_date_trunc_interval='week' -> align partition boundaries to ISO weeks (Monday)
  62. -- p_premake=8 -> always keep 8 future weeks pre-created
  63. -- p_default_table=true -> create a DEFAULT partition catching out-of-range
  64. -- inserts so they never fail silently (RecordConsumeLog
  65. -- only logs errors without retrying)
  66. -- No retention is set: per current requirement we do NOT auto-drop old partitions.
  67. SELECT partman.create_parent(
  68. p_parent_table => 'public.logs',
  69. p_control => 'created_at',
  70. p_type => 'range',
  71. p_interval => '1 week',
  72. p_epoch => 'seconds',
  73. p_date_trunc_interval => 'week',
  74. p_premake => 8,
  75. p_default_table => true
  76. );
  77. -- 4. Core indexes on the parent table ---------------------------------------
  78. -- PG 11+ propagates indexes created on a partitioned parent to all child
  79. -- partitions automatically. These cover the hot query paths in model/log.go
  80. -- (GetAllLogs / GetUserLogs ordering by created_at desc, id desc; lookups by
  81. -- user/model/channel/request/token). Trimmed from the model's 16 index tags to
  82. -- the 6 actually used, cutting index write overhead.
  83. CREATE INDEX IF NOT EXISTS idx_logs_created_at_id ON public.logs (created_at DESC, id DESC);
  84. CREATE INDEX IF NOT EXISTS idx_logs_user_id_created ON public.logs (user_id, created_at DESC);
  85. CREATE INDEX IF NOT EXISTS idx_logs_model_name ON public.logs (model_name);
  86. CREATE INDEX IF NOT EXISTS idx_logs_channel_id ON public.logs (channel_id);
  87. CREATE INDEX IF NOT EXISTS idx_logs_request_id ON public.logs (request_id);
  88. CREATE INDEX IF NOT EXISTS idx_logs_token_id ON public.logs (token_id);
  89. -- 5. Schedule periodic maintenance ------------------------------------------
  90. -- run_maintenance_proc() inspects partman.part_config and premakes the next
  91. -- partitions when needed. Every 30 minutes is more than enough; weekly partitions
  92. -- only need creation roughly once a week. No retention => no drops.
  93. SELECT cron.schedule(
  94. 'log-partition-maint',
  95. '*/30 * * * *',
  96. $$CALL partman.run_maintenance_proc()$$
  97. );