Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: add retention config forworkspace_agent_logs#21039

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
mafredri wants to merge4 commits intomafredri/feat-coderd-db-retention-policy-5
base:mafredri/feat-coderd-db-retention-policy-5
Choose a base branch
Loading
frommafredri/feat-coderd-db-retention-policy-6
Open
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletionscli/testdata/coder_server_--help.golden
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -717,6 +717,12 @@ that data type.
How long connection log entries are retained. Set to 0 to disable
(keep indefinitely).

--workspace-agent-logs-retention duration, $CODER_WORKSPACE_AGENT_LOGS_RETENTION (default: 7d)
How long workspace agent logs are retained. Logs from non-latest
builds are deleted if the agent hasn't connected within this period.
Logs from the latest build are always retained. Set to 0 to disable
automatic deletion.

TELEMETRY OPTIONS:
Telemetry is critical to our ability to improve Coder. We strip all personal
information before sending data to our servers. Please only disable telemetry
Expand Down
5 changes: 5 additions & 0 deletionscli/testdata/server-config.yaml.golden
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -761,3 +761,8 @@ retention:
# an expired key. Set to 0 to disable automatic deletion of expired keys.
# (default: 7d, type: duration)
api_keys: 168h0m0s
# How long workspace agent logs are retained. Logs from non-latest builds are
# deleted if the agent hasn't connected within this period. Logs from the latest
# build are always retained. Set to 0 to disable automatic deletion.
# (default: 7d, type: duration)
workspace_agent_logs: 168h0m0s
4 changes: 4 additions & 0 deletionscoderd/apidoc/docs.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

4 changes: 4 additions & 0 deletionscoderd/apidoc/swagger.json
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

4 changes: 2 additions & 2 deletionscoderd/database/dbauthz/dbauthz.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1784,9 +1784,9 @@ func (q *querier) DeleteOldTelemetryLocks(ctx context.Context, beforeTime time.T
return q.db.DeleteOldTelemetryLocks(ctx, beforeTime)
}

func (q *querier) DeleteOldWorkspaceAgentLogs(ctx context.Context, threshold time.Time) error {
func (q *querier) DeleteOldWorkspaceAgentLogs(ctx context.Context, threshold time.Time)(int64,error) {
if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceSystem); err != nil {
return err
return0,err
}
return q.db.DeleteOldWorkspaceAgentLogs(ctx, threshold)
}
Expand Down
2 changes: 1 addition & 1 deletioncoderd/database/dbauthz/dbauthz_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -3227,7 +3227,7 @@ func (s *MethodTestSuite) TestSystemFunctions() {
}))
s.Run("DeleteOldWorkspaceAgentLogs", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
t := time.Time{}
dbm.EXPECT().DeleteOldWorkspaceAgentLogs(gomock.Any(), t).Return(nil).AnyTimes()
dbm.EXPECT().DeleteOldWorkspaceAgentLogs(gomock.Any(), t).Return(int64(0),nil).AnyTimes()
check.Args(t).Asserts(rbac.ResourceSystem, policy.ActionDelete)
}))
s.Run("InsertWorkspaceAgentStats", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
Expand Down
6 changes: 3 additions & 3 deletionscoderd/database/dbmetrics/querymetrics.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

7 changes: 4 additions & 3 deletionscoderd/database/dbmock/dbmock.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

15 changes: 10 additions & 5 deletionscoderd/database/dbpurge/dbpurge.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,8 +18,7 @@ import (
)

const (
delay = 10 * time.Minute
maxAgentLogAge = 7 * 24 * time.Hour
delay = 10 * time.Minute
// Connection events are now inserted into the `connection_logs` table.
// We'll slowly remove old connection events from the `audit_logs` table.
// The `connection_logs` table is purged based on the configured retention.
Expand DownExpand Up@@ -67,9 +66,14 @@ func New(ctx context.Context, logger slog.Logger, db database.Store, vals *coder
return nil
}

deleteOldWorkspaceAgentLogsBefore := start.Add(-maxAgentLogAge)
if err := tx.DeleteOldWorkspaceAgentLogs(ctx, deleteOldWorkspaceAgentLogsBefore); err != nil {
return xerrors.Errorf("failed to delete old workspace agent logs: %w", err)
var purgedWorkspaceAgentLogs int64
workspaceAgentLogsRetention := vals.Retention.WorkspaceAgentLogs.Value()
if workspaceAgentLogsRetention > 0 {
deleteOldWorkspaceAgentLogsBefore := start.Add(-workspaceAgentLogsRetention)
purgedWorkspaceAgentLogs, err = tx.DeleteOldWorkspaceAgentLogs(ctx, deleteOldWorkspaceAgentLogsBefore)
if err != nil {
return xerrors.Errorf("failed to delete old workspace agent logs: %w", err)
}
}
if err := tx.DeleteOldWorkspaceAgentStats(ctx); err != nil {
return xerrors.Errorf("failed to delete old workspace agent stats: %w", err)
Expand DownExpand Up@@ -149,6 +153,7 @@ func New(ctx context.Context, logger slog.Logger, db database.Store, vals *coder
}

logger.Debug(ctx, "purged old database entries",
slog.F("workspace_agent_logs", purgedWorkspaceAgentLogs),
slog.F("expired_api_keys", expiredAPIKeys),
slog.F("aibridge_records", purgedAIBridgeRecords),
slog.F("connection_logs", purgedConnectionLogs),
Expand Down
90 changes: 89 additions & 1 deletioncoderd/database/dbpurge/dbpurge_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -246,7 +246,11 @@ func TestDeleteOldWorkspaceAgentLogs(t *testing.T) {
// After dbpurge completes, the ticker is reset. Trap this call.

done := awaitDoTick(ctx, t, clk)
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{}, clk)
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
Retention: codersdk.RetentionConfig{
WorkspaceAgentLogs: serpent.Duration(7 * 24 * time.Hour),
},
}, clk)
defer closer.Close()
<-done // doTick() has now run.

Expand DownExpand Up@@ -392,6 +396,90 @@ func mustCreateAgentLogs(ctx context.Context, t *testing.T, db database.Store, a
require.NotEmpty(t, agentLogs, "agent logs must be present")
}

func TestDeleteOldWorkspaceAgentLogsRetention(t *testing.T) {
t.Parallel()

now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)

testCases := []struct {
name string
retentionConfig codersdk.RetentionConfig
logsAge time.Duration
expectDeleted bool
}{
{
name: "RetentionEnabled",
retentionConfig: codersdk.RetentionConfig{
WorkspaceAgentLogs: serpent.Duration(7 * 24 * time.Hour), // 7 days
},
logsAge: 8 * 24 * time.Hour, // 8 days ago
expectDeleted: true,
},
{
name: "RetentionDisabled",
retentionConfig: codersdk.RetentionConfig{
WorkspaceAgentLogs: serpent.Duration(0),
},
logsAge: 60 * 24 * time.Hour, // 60 days ago
expectDeleted: false,
},

{
name: "CustomRetention30Days",
retentionConfig: codersdk.RetentionConfig{
WorkspaceAgentLogs: serpent.Duration(30 * 24 * time.Hour), // 30 days
},
logsAge: 31 * 24 * time.Hour, // 31 days ago
expectDeleted: true,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

ctx := testutil.Context(t, testutil.WaitShort)
clk := quartz.NewMock(t)
clk.Set(now).MustWait(ctx)

oldTime := now.Add(-tc.logsAge)

db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
org := dbgen.Organization(t, db, database.Organization{})
user := dbgen.User(t, db, database.User{})
_ = dbgen.OrganizationMember(t, db, database.OrganizationMember{UserID: user.ID, OrganizationID: org.ID})
tv := dbgen.TemplateVersion(t, db, database.TemplateVersion{OrganizationID: org.ID, CreatedBy: user.ID})
tmpl := dbgen.Template(t, db, database.Template{OrganizationID: org.ID, ActiveVersionID: tv.ID, CreatedBy: user.ID})

ws := dbgen.Workspace(t, db, database.WorkspaceTable{Name: "test-ws", OwnerID: user.ID, OrganizationID: org.ID, TemplateID: tmpl.ID})
wb1 := mustCreateWorkspaceBuild(t, db, org, tv, ws.ID, oldTime, 1)
wb2 := mustCreateWorkspaceBuild(t, db, org, tv, ws.ID, oldTime, 2)
agent1 := mustCreateAgent(t, db, wb1)
agent2 := mustCreateAgent(t, db, wb2)
mustCreateAgentLogs(ctx, t, db, agent1, &oldTime, "agent 1 logs")
mustCreateAgentLogs(ctx, t, db, agent2, &oldTime, "agent 2 logs")

// Run the purge.
done := awaitDoTick(ctx, t, clk)
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
Retention: tc.retentionConfig,
}, clk)
defer closer.Close()
testutil.TryReceive(ctx, t, done)

// Verify results.
if tc.expectDeleted {
assertNoWorkspaceAgentLogs(ctx, t, db, agent1.ID)
} else {
assertWorkspaceAgentLogs(ctx, t, db, agent1.ID, "agent 1 logs")
}
// Latest build logs are always retained.
assertWorkspaceAgentLogs(ctx, t, db, agent2.ID, "agent 2 logs")
})
}
}

//nolint:paralleltest // It uses LockIDDBPurge.
func TestDeleteOldProvisionerDaemons(t *testing.T) {
// TODO: must refactor DeleteOldProvisionerDaemons to allow passing in cutoff
Expand Down
4 changes: 2 additions & 2 deletionscoderd/database/querier.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

13 changes: 8 additions & 5 deletionscoderd/database/queries.sql.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

4 changes: 2 additions & 2 deletionscoderd/database/queries/workspaceagents.sql
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -199,10 +199,10 @@ INSERT INTO
-- name: GetWorkspaceAgentLogSourcesByAgentIDs :many
SELECT * FROM workspace_agent_log_sources WHERE workspace_agent_id = ANY(@ids :: uuid [ ]);

-- If an agent hasn't connectedin thelast 7 days, we purgeit's logs.
-- If an agent hasn't connectedwithin theretention period, we purgeits logs.
-- Exception: if the logs are related to the latest build, we keep those around.
-- Logs can take up a lot of space, so it's important we clean up frequently.
-- name: DeleteOldWorkspaceAgentLogs :exec
-- name: DeleteOldWorkspaceAgentLogs :execrows
WITH
latest_builds AS (
SELECT
Expand Down
16 changes: 16 additions & 0 deletionscodersdk/deployment.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -829,6 +829,11 @@ type RetentionConfig struct {
// Keys are only deleted if they have been expired for at least this duration.
// Defaults to 7 days to preserve existing behavior.
APIKeys serpent.Duration `json:"api_keys" typescript:",notnull"`
// WorkspaceAgentLogs controls how long workspace agent logs are retained.
// Logs are deleted if the agent hasn't connected within this period.
// Logs from the latest build are always retained regardless of age.
// Defaults to 7 days to preserve existing behavior.
WorkspaceAgentLogs serpent.Duration `json:"workspace_agent_logs" typescript:",notnull"`
}

type NotificationsConfig struct {
Expand DownExpand Up@@ -3420,6 +3425,17 @@ Write out the current server config as YAML to stdout.`,
YAML: "api_keys",
Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"),
},
{
Name: "Workspace Agent Logs Retention",
Description: "How long workspace agent logs are retained. Logs from non-latest builds are deleted if the agent hasn't connected within this period. Logs from the latest build are always retained. Set to 0 to disable automatic deletion.",
Flag: "workspace-agent-logs-retention",
Env: "CODER_WORKSPACE_AGENT_LOGS_RETENTION",
Value: &c.Retention.WorkspaceAgentLogs,
Default: "7d",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

What was your thought process for 7d? Is this table particularly egregious in its disk usage?

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I didn't give it any thought beyond retaining existing behavior, whilst making it configurable.

Actually, it's good you called this out because I noticed I need to add more color:

-- If an agent hasn't connected in the last 7 days, we purge it's logs.-- Exception: if the logs are related to the latest build, we keep those around.-- Logs can take up a lot of space, so it's important we clean up frequently.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

OK cool. That should be fine although I think 7d is probably a bit too short, especially if the table is not huge.

Nit:it's ->its

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

That was a copy paste comment from existing code, already fixedfe71946 (#21039). 😉

I think 7d is fine considering the constraint:

  • Latest build logs arealways kept
  • Agent connection is considered

If you want to debug a build, it's quite likely that the logs will be present or old enough that you don't care.

Unfortunately, the original PR did not add much motivation for the value:#6558, but given this has been the value since it's inception, I'm OK with it staying this way.

Group: &deploymentGroupRetention,
YAML: "workspace_agent_logs",
Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"),
},
{
Name: "Enable Authorization Recordings",
Description: "All api requests will have a header including all authorization calls made during the request. " +
Expand Down
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp