How We Automated BI Content Cleanup Across Multiple Analytics Servers
Every enterprise analytics platform sooner or later faces the same challenge. More content is genera 2026-9-15 16:8:46 Author: hackernoon.com(查看原文) 阅读量:16 收藏

Every enterprise analytics platform sooner or later faces the same challenge. More content is generated than removed. More dashboards are created. More data sources are uploaded. More workflows are launched. Projects grow. Teams evolve. People move on. But nothing much actually gets removed.

In small-scale settings, this poses little operational problem. In enterprise-scale environments, however, things become challenging quite quickly. Ineffective use of disk space becomes an issue. Scheduled refreshes continue running for weeks or months after being no longer needed. Backups continue consuming increasing amounts of resources. Performance gradually worsens.

Cleaning up seems the obvious way out. Automated deletion of unused content becomes the tempting solution.

Except...

What if something important ends up being accidentally deleted? Thousands of users will be affected, and their jobs will become a lot harder overnight.

That was the problem our engineering team had to face while working with a large analytics platform featuring 14 servers and hundreds of thousands of users. Neither manual nor fully automated content cleanups were viable options for us. So, the solution we had to design combined manual validation and automation to ensure safety while achieving operational benefits.

The Challenge of Identifying Stale Content

Most organizations tackle content cleanup through one of two approaches.

The first involves manual reviews. Admins check which content is no longer being used and remove it from the platform. This approach works perfectly well in small-scale environments. It's totally impractical in enterprise-scale settings where the number of assets is too large for humans to process manually.

The second approach relies on automated deletion based on inactivity criteria. As long as content remains unused for a specified period, it gets removed automatically.

Unfortunately, different kinds of content behave very differently. Some files get used every day. Others get accessed only once or twice per month. Some are only needed quarterly or annually. Others may appear inactive but still participate in scheduled refreshes or business processes.

As we explored both approaches, we concluded that neither solved the problem effectively. Manual review was operationally impossible. Automated deletion introduced too much risk.

The challenge wasn't identifying stale content. The challenge was doing so safely.

Designing a System for Safe Failure

The principle that ultimately guided the architecture was simple:

Don't delete anything you can't restore.

This requirement shaped every decision that followed.

Traditional cleanup automation typically looks like this:

Identify Stale Content
          ↓
Delete Content

Our system followed a different path:

Ravi Krishna's image-4624d8

By separating quarantine from deletion, we fundamentally changed the system's failure modes. If content was incorrectly classified, it could still be recovered. If a stakeholder realized an asset was still needed, intervention remained possible. The system was intentionally engineered so that its default failure mode was inconvenience rather than data loss.

Building the Content Discovery Engine

The first major component of the platform was the content discovery engine.

Its objective was straightforward:

Identify content that had not been used within a specific period.

At first glance, this sounds simple.

Most platforms expose metadata such as:

  • Last modified date
  • Last accessed date
  • Ownership information
  • Scheduled refresh information
def fetch_all_assets(connections):
    """Iterate server connections, join asset + ownership data."""
    all_assets = []

    query = """
        SELECT a.asset_id, a.last_modified, a.last_accessed,
               a.refresh_success_rate, u.user_id AS owner_id, u.is_active AS owner_active
        FROM assets a
        LEFT JOIN users u ON a.owner_id = u.user_id
        WHERE a.deleted_at IS NULL
    """

    for conn_info in connections:
        try:
            with psycopg2.connect(**conn_info) as conn, conn.cursor() as cur:
                cur.execute(query)
                for row in cur.fetchall():
                    all_assets.append(dict(zip(
                        ["asset_id", "last_modified", "last_accessed",
                         "refresh_success_rate", "owner_id", "owner_active"],
                        row
                    )))
        except Exception as e:
            logger.error(f"Skipping {conn_info['host']}: {e}")  # one bad server shouldn't halt the run

    return all_assets

However, reality proved more complicated. Certain content types generated incomplete usage data. Embedded dashboards sometimes produced no meaningful activity signals. Some assets were accessed indirectly through external applications. Others participated in automated processes without receiving direct user traffic.

As a result, we had to move beyond simple metadata checks and evaluate multiple signals simultaneously.

The platform incorporated factors such as:

  • Access frequency
  • Refresh activity
  • Embedded usage patterns
  • Ownership status
def staleness_score(asset):
    score = 0
    score += min(asset.days_since_last_access / 90, 1.0) * 0.35
    score += (1 - asset.refresh_success_rate) * 0.25
    score += (0 if asset.embedded_usage_detected else 1) * 0.20
    score += (1 if asset.owner_inactive else 0) * 0.20
    return score  # 0 = active, 1 = strong stale candidate

Rather than relying on a single field, content activity became a composite score built from multiple indicators.

Why a Universal Retention Policy Fails

Another challenge emerged early in development. Initially, we considered applying a universal inactivity threshold to all content. That quickly proved problematic.

Different environments operate under different business rhythms. Development environments tend to have short lifecycles. Financial reporting environments often follow quarterly cycles. Compliance-related assets may only be accessed once per year. Applying the same threshold everywhere creates unnecessary risk.

To address this, we introduced configurable retention policies. Retention could vary based on:

  • Environment type
  • Project classification
  • Known usage patterns
  • Business requirements

Environment type

Inactivity threshold before quarantine

Grace period

Development/sandbox

~30 days

3 days

Standard reporting

~90 days

14 days

Financial/quarterly cycles

~120 days

30 days

Compliance/audit

~365 days

45 days

This flexibility allowed lifecycle management to reflect operational reality rather than arbitrary platform rules.

The Importance of the Quarantine Layer

Quarantine became the most important stage of the entire workflow.

ACTIVE → FLAGGED → QUARANTINED → BACKED_UP → DELETED
              ↑           ↓
              └── RESTORED (owner intervention)

Once content was identified as stale, it was moved into a controlled quarantine environment.

At that stage:

  • User access was blocked
  • Scheduled refreshes were disabled
  • Automated tasks were suspended
  • Notifications were sent to content owners

Importantly, the content itself remained intact.

This created a validation period.

Stakeholders could review the content and determine whether it was still required.

If restoration was needed, the process could be initiated before any destructive actions occurred.

Quarantine transformed cleanup from an irreversible decision into a reversible workflow.

Designing for Recoverability Before Deletion

A common mistake in automation projects is focusing exclusively on successful execution.

We chose to focus on recoverability instead.

Before any content entered the deletion phase, it was backed up.

def validate_backup(asset_id, backup_path):
    checksum_original = compute_checksum(asset_id)
    checksum_backup = compute_checksum(backup_path)
    return checksum_original == checksum_backup

The backup process supported:

  • Incremental transfers
  • File compression
  • Large file handling
  • Integrity verification
  • Restoration validation

Deletion only became possible after backup validation succeeded.

This ensured that every removal remained reversible.

Recoverability wasn't treated as an optional feature.

It was a prerequisite.

Managing Orphan Ownership

Another challenge involved ownership. Over time, people leave organizations. Teams get reorganized. Projects change hands. As a result, some content no longer has a clearly identifiable owner.

This creates a serious problem for lifecycle management. Who receives notifications? Who approves restoration? Who determines whether content remains valuable?

To address this, we introduced an ownership reassignment process. Before quarantine, orphaned content was reassigned to team-level ownership structures.

def resolve_owner(asset):
    if asset.owner.is_active:
        return asset.owner
    return get_team_owner(asset.project_id) or "unclaimed-review-queue"

This gave active stakeholders an opportunity to claim responsibility before any lifecycle actions occurred.

Ensuring Auditability

Lifecycle management without auditability quickly becomes problematic.

Every action taken by the platform needed to be traceable.

Every decision needed to be explainable.

The audit layer captured:

  • Content identifier
  • Ownership information
  • Last access date
  • Quarantine date
  • Backup status
  • Deletion date
  • Restoration requests
  • Automation run identifiers
{
  "asset_id": "Dashboard-0192",
  "state": "QUARANTINED",
  "previous_owner": "team:analytics-core",
  "last_access_days_ago": 118,
  "staleness_score": 0.81,
  "backup_status": "validated",
  "quarantine_date": "2026-04-02",
  "restoration_requested": false,
  "automation_run_id": "run-20260402-0007"
}

This allowed us to reconstruct the complete lifecycle of every managed asset.

More importantly, it enabled us to answer difficult questions months later.

Why was something removed?

Who owned it?

When was it last accessed?

Was it backed up?

Without auditability, those questions become difficult.

With auditability, they become routine.

What We Learned About Platform Engineering

One of the most interesting lessons from this project was that content lifecycle management is often misunderstood.

Most organizations view it as an operational activity.

In reality, it behaves much more like a platform engineering problem.

The objective is not simply deleting unused assets.

The objective is designing systems that can safely remove assets without creating risk.

That requires:

  • Discovery mechanisms
  • Backup systems
  • Audit capabilities
  • Ownership management
  • Policy enforcement

Interestingly, these same principles appear across many other engineering domains:

  • Cloud governance
  • Data lake management
  • Artifact retention
  • Resource cleanup automation

The underlying challenge remains remarkably consistent.

How do you automate removal without introducing unacceptable risk?

The Bigger Lesson

The most valuable lesson from this project extends beyond analytics platforms.

Most automation systems are designed primarily for successful execution.

The downside of that approach is that failure scenarios often receive less attention.

We deliberately reversed that thinking.

The platform was designed around safe failure.

What happens if content is misclassified?

What happens if ownership data is incorrect?

What happens if restoration is required?

By designing for those scenarios first, we were able to automate a process that would otherwise have remained manual.

Conclusion

Designing a content lifecycle management platform taught us that automation is not fundamentally about removing manual work. It's about creating systems that remain safe even when they make mistakes.

By introducing quarantine, recoverability, ownership management, and auditability into the cleanup process, we transformed a risky deletion problem into a predictable engineering workflow.

The result was not simply a cleaner platform. It was a more resilient one. And in large-scale platform engineering, resilience is often more valuable than efficiency.


Author’s note: This article reflects my personal views and is not written on behalf of, endorsed by, or intended to represent the views of my employer. It is intended for general informational purposes and does not constitute legal, technical, or other professional advice.


文章来源: https://hackernoon.com/how-we-automated-bi-content-cleanup-across-multiple-analytics-servers?source=rss
如有侵权请联系:admin#unsafe.sh