Skip to main content

Escalabilidade e multitenância

Projete sua implantação do SDK Copilot para atender a vários usuários, manipular sessões simultâneas e dimensionar horizontalmente em toda a infraestrutura. Este guia aborda padrões de isolamento de sessão, topologias de dimensionamento e práticas recomendadas de produção.

Melhor para: Desenvolvedores de plataforma, construtores de SaaS, qualquer implantação que atende a mais de um punhado de usuários simultâneos.

Conceitos principais

Antes de escolher um padrão, entenda três dimensões de dimensionamento:

Diagrama: Fluxograma mostrando o processo descrito.

Padrões de isolamento de sessões

Padrão 1: CLI isolada por usuário

Cada usuário obtém sua própria instância de servidor da CLI. Isolamento mais forte: as sessões, a memória e os processos de um usuário são completamente separados.

Diagrama: Fluxograma mostrando o processo descrito.

Quando usar:

  • SaaS multilocatário em que o isolamento de dados é crítico
  • Usuários com credenciais de autenticação diferentes
  • Requisitos de conformidade (SOC 2, HIPAA)
// CLI pool manager — one CLI per user
class CLIPool {
    private instances = new Map<string, { client: CopilotClient; port: number }>();
    private nextPort = 5000;

    async getClientForUser(userId: string, token?: string): Promise<CopilotClient> {
        if (this.instances.has(userId)) {
            return this.instances.get(userId)!.client;
        }

        const port = this.nextPort++;

        // Spawn a dedicated CLI for this user
        await spawnCLI(port, token);

        const client = new CopilotClient({
            cliUrl: `localhost:${port}`,
        });

        this.instances.set(userId, { client, port });
        return client;
    }

    async releaseUser(userId: string): Promise<void> {
        const instance = this.instances.get(userId);
        if (instance) {
            await instance.client.stop();
            this.instances.delete(userId);
        }
    }
}

Padrão 2: CLI compartilhada com isolamento de sessão

Vários usuários compartilham um servidor da CLI, mas têm sessões isoladas por meio de IDs de sessão exclusivas. Mais leve em recursos, mas isolamento mais fraco.

Diagrama: Fluxograma mostrando o processo descrito.

Quando usar:

  • Ferramentas internas com usuários confiáveis
  • Ambientes restritos a recursos
  • Requisitos de isolamento mais baixos
const sharedClient = new CopilotClient({
    cliUrl: "localhost:4321",
});

// Enforce session isolation through naming conventions
function getSessionId(userId: string, purpose: string): string {
    return `${userId}-${purpose}-${Date.now()}`;
}

// Access control: ensure users can only access their own sessions
async function resumeSessionWithAuth(
    sessionId: string,
    currentUserId: string
): Promise<Session> {
    const [sessionUserId] = sessionId.split("-");
    if (sessionUserId !== currentUserId) {
        throw new Error("Access denied: session belongs to another user");
    }
    return sharedClient.resumeSession(sessionId);
}

Padrão 3: sessões compartilhadas (colaborativas)

Vários usuários interagem com a mesma sessão, como uma sala de chat compartilhada com o Copilot.

Diagrama: Fluxograma mostrando o processo descrito.

Quando usar:

  • Ferramentas de colaboração em equipe
  • Sessões de revisão de código compartilhado
  • Emparelhar assistentes de programação

⚠️Importante: O SDK não fornece bloqueio de sessão interno. Você deve serializar o acesso para evitar gravações simultâneas na mesma sessão.

import Redis from "ioredis";

const redis = new Redis();

async function withSessionLock<T>(
    sessionId: string,
    fn: () => Promise<T>,
    timeoutSec = 300
): Promise<T> {
    const lockKey = `session-lock:${sessionId}`;
    const lockId = crypto.randomUUID();

    // Acquire lock
    const acquired = await redis.set(lockKey, lockId, "NX", "EX", timeoutSec);
    if (!acquired) {
        throw new Error("Session is in use by another user");
    }

    try {
        return await fn();
    } finally {
        // Release lock (only if we still own it)
        const currentLock = await redis.get(lockKey);
        if (currentLock === lockId) {
            await redis.del(lockKey);
        }
    }
}

// Usage: serialize access to shared session
app.post("/team-chat", authMiddleware, async (req, res) => {
    const result = await withSessionLock("team-project-review", async () => {
        const session = await client.resumeSession("team-project-review");
        return session.sendAndWait({ prompt: req.body.message });
    });

    res.json({ content: result?.data.content });
});

Comparação de padrões de isolamento

CLI isolada por usuárioCLI compartilhada + Isolamento de sessãoSessões compartilhadas
Isolation
✅ Concluído
⚠️ Lógico
❌ Compartilhado
Uso de recursosAlta (CLI por usuário)Baixo (uma CLI)Baixo (uma CLI + sessão)
ComplexidadeMediumBaixoAlto (bloqueio)
Flexibilidade de autenticação
✅ Tokens por usuário
⚠️ Token de serviço
⚠️ Token de serviço
Mais adequado paraSaaS multilocatárioFerramentas internasColaboração

Escalabilidade horizontal

Vários servidores CLI por trás de um balanceador de carga

Diagrama: Fluxograma mostrando o processo descrito.

Requisito de chave: O estado da sessão deve estar no armazenamento compartilhado para que qualquer servidor da CLI possa retomar qualquer sessão.

// Route sessions to CLI servers
class CLILoadBalancer {
    private servers: string[];
    private currentIndex = 0;

    constructor(servers: string[]) {
        this.servers = servers;
    }

    // Round-robin selection
    getNextServer(): string {
        const server = this.servers[this.currentIndex];
        this.currentIndex = (this.currentIndex + 1) % this.servers.length;
        return server;
    }

    // Sticky sessions: same user always hits same server
    getServerForUser(userId: string): string {
        const hash = this.hashCode(userId);
        return this.servers[hash % this.servers.length];
    }

    private hashCode(str: string): number {
        let hash = 0;
        for (let i = 0; i < str.length; i++) {
            hash = (hash << 5) - hash + str.charCodeAt(i);
            hash |= 0;
        }
        return Math.abs(hash);
    }
}

const lb = new CLILoadBalancer([
    "cli-1:4321",
    "cli-2:4321",
    "cli-3:4321",
]);

app.post("/chat", async (req, res) => {
    const server = lb.getServerForUser(req.user.id);
    const client = new CopilotClient({ cliUrl: server });

    const session = await client.createSession({
        sessionId: `user-${req.user.id}-chat`,
        model: "gpt-4.1",
    });

    const response = await session.sendAndWait({ prompt: req.body.message });
    res.json({ content: response?.data.content });
});

Sessões persistentes versus armazenamento compartilhado

Diagrama: Fluxograma mostrando o processo descrito.

Sessões persistentes são mais simples: mantêm os usuários vinculados a servidores específicos de CLI. Não é necessário armazenamento compartilhado, mas a distribuição de carga é desigual.

O armazenamento compartilhado permite que qualquer CLI manipule qualquer sessão. Melhor distribuição de carga, mas requer armazenamento em rede para ~/.copilot/session-state/.

Escalonamento vertical

Ajustando um único servidor da CLI

Um único servidor da CLI pode lidar com muitas sessões simultâneas. Principais considerações:

Diagrama: Fluxograma mostrando o processo descrito.

O gerenciamento do ciclo de vida da sessão é fundamental para o dimensionamento vertical:

// Limit concurrent active sessions
class SessionManager {
    private activeSessions = new Map<string, Session>();
    private maxConcurrent: number;

    constructor(maxConcurrent = 50) {
        this.maxConcurrent = maxConcurrent;
    }

    async getSession(sessionId: string): Promise<Session> {
        // Return existing active session
        if (this.activeSessions.has(sessionId)) {
            return this.activeSessions.get(sessionId)!;
        }

        // Enforce concurrency limit
        if (this.activeSessions.size >= this.maxConcurrent) {
            await this.evictOldestSession();
        }

        // Create or resume
        const session = await client.createSession({
            sessionId,
            model: "gpt-4.1",
        });

        this.activeSessions.set(sessionId, session);
        return session;
    }

    private async evictOldestSession(): Promise<void> {
        const [oldestId] = this.activeSessions.keys();
        const session = this.activeSessions.get(oldestId)!;
        // Session state is persisted automatically — safe to disconnect
        await session.disconnect();
        this.activeSessions.delete(oldestId);
    }
}

Sessões efêmeras versus persistentes

Diagrama: Fluxograma mostrando o processo descrito.

Sessões efêmeras

Para endpoints de API sem estado, em que cada solicitação é independente:

app.post("/api/analyze", async (req, res) => {
    const session = await client.createSession({
        model: "gpt-4.1",
    });

    try {
        const response = await session.sendAndWait({
            prompt: req.body.prompt,
        });
        res.json({ result: response?.data.content });
    } finally {
        await session.disconnect();  // Clean up immediately
    }
});

Sessões persistentes

Para interfaces de conversa ou fluxos de trabalho de execução longa:

// Create a resumable session
app.post("/api/chat/start", async (req, res) => {
    const sessionId = `user-${req.user.id}-${Date.now()}`;

    const session = await client.createSession({
        sessionId,
        model: "gpt-4.1",
        infiniteSessions: {
            enabled: true,
            backgroundCompactionThreshold: 0.80,
        },
    });

    res.json({ sessionId });
});

// Continue the conversation
app.post("/api/chat/message", async (req, res) => {
    const session = await client.resumeSession(req.body.sessionId);
    const response = await session.sendAndWait({ prompt: req.body.message });

    res.json({ content: response?.data.content });
});

// Clean up when done
app.post("/api/chat/end", async (req, res) => {
    await client.deleteSession(req.body.sessionId);
    res.json({ success: true });
});

Implantações de contêiner

Kubernetes com armazenamento persistente

apiVersion: apps/v1
kind: Deployment
metadata:
  name: copilot-cli
spec:
  replicas: 3
  selector:
    matchLabels:
      app: copilot-cli
  template:
    metadata:
      labels:
        app: copilot-cli
    spec:
      containers:
        - name: copilot-cli
          image: your-registry/copilot-cli:latest  # See backend-services.md for how to build and push this image
          args: ["--headless", "--host", "0.0.0.0", "--port", "4321"]
          env:
            - name: COPILOT_GITHUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: copilot-secrets
                  key: github-token
          ports:
            - containerPort: 4321
          volumeMounts:
            - name: session-state
              mountPath: /root/.copilot/session-state
      volumes:
        - name: session-state
          persistentVolumeClaim:
            claimName: copilot-sessions-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: copilot-cli
spec:
  selector:
    app: copilot-cli
  ports:
    - port: 4321
      targetPort: 4321

Diagrama: Fluxograma mostrando o processo descrito.

Instâncias de Contêiner do Azure

containers:
  - name: copilot-cli
    image: your-registry/copilot-cli:latest  # See backend-services.md for how to build and push this image
    command: ["copilot", "--headless", "--host", "0.0.0.0", "--port", "4321"]
    volumeMounts:
      - name: session-storage
        mountPath: /root/.copilot/session-state

volumes:
  - name: session-storage
    azureFile:
      shareName: copilot-sessions
      storageAccountName: myaccount

Lista de verificação de produção

Diagrama: Fluxograma mostrando o processo descrito.

PreocupaçãoRecomendação
Limpeza de sessãoExecute uma limpeza periódica para excluir sessões com mais tempo do que o TTL
Verificações de integridadeExecutar ping no servidor da CLI periodicamente; reiniciar se não responder
ArmazenamentoMontar volumes persistentes para ~/.copilot/session-state/
SegredosUse o gerenciador de segredos da sua plataforma (Vault, K8s Secrets, etc.)
MonitorizaçãoAcompanhar contagem de sessão ativa, latência de resposta, taxas de erro
LockingUsar Redis ou semelhante para acesso de sessão compartilhado
DesligamentoEncerrar sessões ativas antes de parar os servidores de CLI

Limitações

LimitationDetalhes
Nenhum bloqueio de sessão internoImplementar o bloqueio no nível do aplicativo para acesso simultâneo
Nenhum balanceamento de carga internoUsar LB externo ou malha de serviço
O estado da sessão é baseado em arquivoRequer sistema de arquivos compartilhado para configurações de vários servidores
Tempo limite ocioso de 30 minutosAs sessões sem atividade são limpas automaticamente pela CLI
A CLI é de processo únicoEscale adicionando mais instâncias do servidor CLI, não threads

Próximas Etapas