# πŸ—„οΈ Π‘Π°Π·Π° Π΄Π°Π½Π½Ρ‹Ρ… ОписаниС Ρ€Π°Π±ΠΎΡ‚Ρ‹ с PostgreSQL Π² ChatBot. ## πŸ“Š Π‘Ρ…Π΅ΠΌΠ° Π±Π°Π·Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ… ### Π’Π°Π±Π»ΠΈΡ†Ρ‹ #### chat_sessions Π₯Ρ€Π°Π½ΠΈΡ‚ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ сСссиях Ρ‡Π°Ρ‚ΠΎΠ². | Колонка | Π’ΠΈΠΏ | Constraints | ОписаниС | |---------|-----|-------------|----------| | id | SERIAL | PRIMARY KEY | Auto-increment ID | | session_id | VARCHAR(50) | UNIQUE, NOT NULL | Π£Π½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ | | chat_id | BIGINT | NOT NULL, INDEXED | Telegram chat ID | | chat_type | VARCHAR(20) | NOT NULL | Π’ΠΈΠΏ Ρ‡Π°Ρ‚Π° | | chat_title | VARCHAR(200) | NULL | НазваниС Ρ‡Π°Ρ‚Π° | | model | VARCHAR(100) | NULL | AI модСль | | created_at | TIMESTAMP | NOT NULL | Π”Π°Ρ‚Π° создания | | last_updated_at | TIMESTAMP | NOT NULL | ПослСднСС ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ | **Π˜Π½Π΄Π΅ΠΊΡΡ‹:** ```sql CREATE UNIQUE INDEX idx_chat_sessions_session_id ON chat_sessions(session_id); CREATE INDEX idx_chat_sessions_chat_id ON chat_sessions(chat_id); ``` #### chat_messages Π₯Ρ€Π°Π½ΠΈΡ‚ ΠΈΡΡ‚ΠΎΡ€ΠΈΡŽ сообщСний. | Колонка | Π’ΠΈΠΏ | Constraints | ОписаниС | |---------|-----|-------------|----------| | id | SERIAL | PRIMARY KEY | Auto-increment ID | | session_id | INTEGER | FK, NOT NULL | Бсылка Π½Π° сСссию | | content | VARCHAR(10000) | NOT NULL | ВСкст сообщСния | | role | VARCHAR(20) | NOT NULL | user/assistant/system | | message_order | INTEGER | NOT NULL | ΠŸΠΎΡ€ΡΠ΄ΠΎΠΊ Π² Π΄ΠΈΠ°Π»ΠΎΠ³Π΅ | | created_at | TIMESTAMP | NOT NULL | ВрСмя создания | **Foreign Keys:** ```sql FOREIGN KEY (session_id) REFERENCES chat_sessions(id) ON DELETE CASCADE ``` **Π˜Π½Π΄Π΅ΠΊΡΡ‹:** ```sql CREATE INDEX idx_chat_messages_session_id ON chat_messages(session_id); CREATE INDEX idx_chat_messages_created_at ON chat_messages(created_at); CREATE INDEX idx_chat_messages_session_order ON chat_messages(session_id, message_order); ``` ## πŸ”„ Entity Framework Core ### DbContext ```csharp public class ChatBotDbContext : DbContext { public DbSet ChatSessions { get; set; } public DbSet ChatMessages { get; set; } } ``` ### ΠšΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ ```csharp protected override void OnModelCreating(ModelBuilder modelBuilder) { // ChatSessionEntity modelBuilder.Entity(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.SessionId).IsRequired().HasMaxLength(50); entity.HasIndex(e => e.SessionId).IsUnique(); entity.HasIndex(e => e.ChatId); entity.HasMany(e => e.Messages) .WithOne(e => e.Session) .HasForeignKey(e => e.SessionId) .OnDelete(DeleteBehavior.Cascade); }); // ChatMessageEntity modelBuilder.Entity(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.Content).IsRequired().HasMaxLength(10000); entity.HasIndex(e => e.SessionId); entity.HasIndex(e => new { e.SessionId, e.MessageOrder }); }); } ``` ### ΠœΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ #### Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ ```bash dotnet ef migrations add InitialCreate --project ChatBot ``` #### ΠŸΡ€ΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΉ ```bash # Π’Ρ€ΡƒΡ‡Π½ΡƒΡŽ dotnet ef database update --project ChatBot # АвтоматичСски ΠΏΡ€ΠΈ запускС (DatabaseInitializationService) ``` #### ΠžΡ‚ΠΊΠ°Ρ‚ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ ```bash dotnet ef database update PreviousMigration --project ChatBot ``` #### Π£Π΄Π°Π»Π΅Π½ΠΈΠ΅ послСднСй ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ ```bash dotnet ef migrations remove --project ChatBot ``` ## πŸ”Œ ΠŸΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ ΠΊ Π‘Π” ### Connection String ``` Host={host};Port={port};Database={name};Username={user};Password={password} ``` **ΠŸΡ€ΠΈΠΌΠ΅Ρ€:** ``` Host=localhost;Port=5432;Database=chatbot;Username=chatbot;Password=secret ``` ### ΠšΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡ Π² Program.cs ```csharp builder.Services.AddDbContext( (serviceProvider, options) => { var dbSettings = serviceProvider .GetRequiredService>() .Value; options.UseNpgsql( dbSettings.ConnectionString, npgsqlOptions => { npgsqlOptions.CommandTimeout(dbSettings.CommandTimeout); } ); } ); ``` ### Connection Pooling Npgsql автоматичСски ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ connection pooling: ``` Max Pool Size=100 Min Pool Size=1 Connection Lifetime=300 Connection Idle Lifetime=300 ``` ## πŸ“ Repository Pattern ### Interface ```csharp public interface IChatSessionRepository { Task GetByChatIdAsync(long chatId); Task CreateAsync(ChatSessionEntity session); Task UpdateAsync(ChatSessionEntity session); Task DeleteAsync(int id); Task> GetAllAsync(); } ``` ### Implementation ```csharp public class ChatSessionRepository : IChatSessionRepository { private readonly ChatBotDbContext _context; public async Task GetByChatIdAsync(long chatId) { return await _context.ChatSessions .Include(s => s.Messages) .FirstOrDefaultAsync(s => s.ChatId == chatId); } public async Task CreateAsync(ChatSessionEntity session) { _context.ChatSessions.Add(session); await _context.SaveChangesAsync(); return session; } } ``` ## πŸš€ ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΡ запросов ### Eager Loading ```csharp // Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° с сообщСниями var session = await _context.ChatSessions .Include(s => s.Messages) .FirstOrDefaultAsync(s => s.ChatId == chatId); ``` ### Projections ```csharp // Волько Π½ΡƒΠΆΠ½Ρ‹Π΅ поля var sessionInfo = await _context.ChatSessions .Where(s => s.ChatId == chatId) .Select(s => new { s.SessionId, s.Model }) .FirstOrDefaultAsync(); ``` ### AsNoTracking ```csharp // Read-only запросы var sessions = await _context.ChatSessions .AsNoTracking() .ToListAsync(); ``` ## πŸ”§ ΠžΠ±ΡΠ»ΡƒΠΆΠΈΠ²Π°Π½ΠΈΠ΅ Π‘Π” ### Vacuum (очистка) ```sql VACUUM ANALYZE chat_sessions; VACUUM ANALYZE chat_messages; ``` ### Бтатистика ```sql SELECT schemaname, tablename, n_live_tup, n_dead_tup FROM pg_stat_user_tables WHERE tablename IN ('chat_sessions', 'chat_messages'); ``` ### Π Π°Π·ΠΌΠ΅Ρ€ Ρ‚Π°Π±Π»ΠΈΡ† ```sql SELECT tablename, pg_size_pretty(pg_total_relation_size(tablename::regclass)) as size FROM pg_tables WHERE schemaname = 'public'; ``` ## πŸ›‘οΈ Π‘Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡ‚ΡŒ ### SQL Injection Prevention Entity Framework Core автоматичСски ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈΠ·ΡƒΠ΅Ρ‚ запросы: ```csharp // βœ… БСзопасно var session = await _context.ChatSessions .Where(s => s.ChatId == chatId) .FirstOrDefaultAsync(); ``` ### ΠŸΡ€Π°Π²Π° ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π‘Π” ```sql -- Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ CREATE USER chatbot WITH PASSWORD 'secure_password'; -- Π’Ρ‹Π΄Π°Ρ‡Π° ΠΏΡ€Π°Π² GRANT CONNECT ON DATABASE chatbot TO chatbot; GRANT USAGE ON SCHEMA public TO chatbot; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO chatbot; GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO chatbot; ``` ## πŸ“Š ΠœΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³ ### Active Connections ```sql SELECT count(*) FROM pg_stat_activity WHERE datname = 'chatbot'; ``` ### Long Running Queries ```sql SELECT pid, now() - pg_stat_activity.query_start AS duration, query FROM pg_stat_activity WHERE state = 'active' ORDER BY duration DESC; ``` ### Locks ```sql SELECT * FROM pg_locks WHERE NOT granted; ``` ## πŸ”„ Backup & Restore ### Backup ```bash # ΠŸΠΎΠ»Π½Ρ‹ΠΉ backup pg_dump -U chatbot chatbot > backup.sql # Волько схСма pg_dump -U chatbot --schema-only chatbot > schema.sql # Волько Π΄Π°Π½Π½Ρ‹Π΅ pg_dump -U chatbot --data-only chatbot > data.sql ``` ### Restore ```bash # ВосстановлСниС psql -U chatbot chatbot < backup.sql ``` ## πŸ“š Π‘ΠΌ. Ρ‚Π°ΠΊΠΆΠ΅ - [МодСли Π΄Π°Π½Π½Ρ‹Ρ…](./data-models.md) - [ΠšΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡ](../configuration.md) - [Установка](../installation.md)