주요 업그레이드 참고 사항

Mattermost v11.11번역일: 2026-09-02원본 보기
중요

Mattermost Server v10.11 Extended Support Release에 대한 지원은 2026년 8월 15일에 수명 주기가 종료되었습니다. Mattermost Server v11.7 이상으로 업그레이드해야 합니다.

이 페이지 하단의 추가 업그레이드 참고 사항도 함께 확인하시기 바랍니다.

다음 버전보다 이전 버전에서 업그레이드하는 경우확인할 내용
v11.10

v11.10.1부터 갱신된 glibc-openssl-fips 번들에는 HMAC 연산에 대해 FIPS 최소 키 길이 112비트를 강제하는 새 OpenSSL 빌드가 포함됩니다. PostgreSQL 드라이버(lib/pq)는 SCRAM 인증 과정에서 데이터베이스 비밀번호를 HMAC 키로 전달하므로, 112비트(ASCII 14자)보다 짧은 비밀번호는 이제 연결 시 정상적인 실패가 아니라 패닉을 일으킵니다. 업그레이드하기 전에 FIPS 배포 환경에서는 SqlSettings.DataSource의 비밀번호가 14자 이상인지 확인하고, 더 짧다면 PostgreSQL에서 교체하세요. 일반(비 FIPS) 빌드는 영향을 받지 않습니다.

이 마이그레이션은 PropertyValues 테이블에 GroupID, UpdateAt, ID 컬럼을 포괄하는 새 복합 인덱스 idx_propertyvalues_groupid_updateat_id를 추가합니다. PropertyValues 테이블은 최근 Mattermost 버전에서 도입된 Properties/Custom Attributes 기능의 일부입니다. 이 인덱스는 GroupID와 UpdateAt으로 필터링하거나 정렬하는 조회 및 범위 스캔의 쿼리 성능을 개선하며, 이는 해당 기능에서 흔히 쓰이는 접근 패턴입니다. 인덱스는 CONCURRENTLY로 생성되므로 빌드 과정에서 PropertyValues 테이블에 대한 동시 읽기나 쓰기를 차단하지 않습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- morph:nontransactional
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_propertyvalues_groupid_updateat_id
    ON PropertyValues(GroupID, UpdateAt, ID);
-- morph:nontransactional
DROP INDEX CONCURRENTLY IF EXISTS idx_propertyvalues_groupid_updateat_id;

이 마이그레이션은 useraccesstokens 테이블에 널 허용 컬럼 lastnotifiedat(bigint)를 추가합니다. 이 컬럼은 특정 사용자 액세스 토큰에 대해 마지막으로 알림이 전송된 시점을 추적해 토큰 관련 알림 로직을 개선하기 위한 것입니다. 컬럼이 기본값 없이 널을 허용하므로 PostgreSQL은 카탈로그만 변경하는 작업을 수행하며, 테이블 재작성이 발생하지 않고 잠금도 수 밀리초 동안만 유지됩니다. 이전 버전의 Mattermost는 새 컬럼을 단순히 무시하므로 롤링 업그레이드 전 구간에서 호환성이 유지됩니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- Up migration
ALTER TABLE useraccesstokens ADD COLUMN IF NOT EXISTS lastnotifiedat bigint;

-- Down migration
ALTER TABLE useraccesstokens DROP COLUMN IF EXISTS lastnotifiedat;
v11.9

Mattermost v11.9는 OAuth Dynamic Client Registration(DCR)에서 리디렉션 URI 허용 목록 패턴을 매칭하는 방식을 변경합니다. 패턴은 이제 문자열 전체에 대한 glob이 아니라 URL 구성 요소별(스킴, 호스트, 경로, 쿼리)로 평가됩니다. 그 결과 https://*.example.com/** 같은 패턴은 쿼리 문자열이 포함된 리디렉션 URI(예: https://app.example.com/callback?tenant=foo)와 더 이상 일치하지 않습니다. 쿼리 문자열이 없는 리디렉션 URI는 기존대로 일치합니다.

쿼리 문자열이 포함된 리디렉션 URI와 함께 DCR을 사용하는 관리자는 허용 목록을 갱신해야 합니다. 쿼리 문자열이 있는 URI와 없는 URI를 모두 허용하려면 다음 두 항목을 각각 추가하세요.

  • https://*.example.com/** — 쿼리 문자열이 없는 리디렉션 URI와 일치합니다.
  • https://*.example.com/**?** — 쿼리 문자열이 있는 모든 리디렉션 URI와 일치합니다.

쿼리 구성 요소(예: ?**)를 포함한 패턴은 쿼리 문자열을 가진 URI만 일치시키며, 쿼리 문자열이 없는 URI에는 일치하지 않습니다. 두 경우를 모두 포괄하려면 두 항목이 모두 필요합니다.

Mattermost 11.9.0은 incomingwebhooks 테이블에 새 컬럼을 추가하는 스키마 변경을 도입합니다. 컬럼을 추가할 때 incomingwebhooks 테이블에 AccessExclusiveLock이 걸리지만, PostgreSQL 11 이상은 비휘발성 상수 기본값을 가진 ADD COLUMN을 메타데이터 전용 작업으로 처리하므로 잠금은 마이크로초 단위로만 유지되며 행 수에 비례하지 않습니다. 동시 읽기와 쓰기는 카탈로그 항목이 갱신되는 순간에만 차단되었다가 즉시 해제되므로 동시 작업에 미치는 영향은 무시할 수 있는 수준입니다. 이 릴리스로 업그레이드할 때 무중단 업그레이드가 가능합니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- up
ALTER TABLE incomingwebhooks ADD COLUMN IF NOT EXISTS lastused bigint NOT NULL DEFAULT 0;
-- down
ALTER TABLE incomingwebhooks DROP COLUMN IF EXISTS lastused;

이 마이그레이션은 VoIP 장치 식별자를 사용자 세션에 연결할 수 있도록 sessions 테이블에 새 컬럼 voipdeviceid(VARCHAR(512) NOT NULL DEFAULT '')를 추가합니다. 이 컬럼은 빈 문자열을 기본값으로 정의되어 기존 행과 구버전 서버가 기록한 행이 데이터 백필 없이도 NOT NULL 제약을 자동으로 충족합니다. PostgreSQL 11 이상에서는 상수 기본값을 가진 컬럼 추가가 메타데이터 전용 작업이므로 테이블 재작성이 발생하지 않고, sessions에 대한 ACCESS EXCLUSIVE 잠금도 짧은 시간(보통 밀리초 단위)만 유지됩니다. 대규모 배포 환경의 관리자는 sessions가 가장 빈번하게 접근되는 테이블 중 하나이며, 이미 해당 테이블에 잠금을 보유한 장시간 트랜잭션이 있으면 마이그레이션 중 잠금 대기 시간이 일시적으로 늘어날 수 있다는 점에 유의해야 합니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- up
ALTER TABLE sessions ADD COLUMN IF NOT EXISTS voipdeviceid VARCHAR(512) NOT NULL DEFAULT '';

-- down
ALTER TABLE sessions DROP COLUMN IF EXISTS voipdeviceid;

이 마이그레이션은 AccessControlPolicies 테이블에 Type과 Id 컬럼을 포괄하는 복합 인덱스 idx_access_control_policies_type_id를 추가합니다. 이 인덱스는 타입과 식별자로 액세스 제어 정책을 필터링하거나 조회할 때의 쿼리 성능을 개선하기 위한 것입니다. 인덱스는 CREATE INDEX CONCURRENTLY로 생성되므로 AccessControlPolicies 테이블의 읽기나 쓰기를 차단하지 않고 백그라운드에서 만들어집니다. 이 변경으로 수정되거나 제거되는 컬럼, 제약 조건, 기존 인덱스는 없습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- Up migration (PostgreSQL)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_access_control_policies_type_id ON AccessControlPolicies(Type, Id);
-- Down migration (PostgreSQL)
DROP INDEX CONCURRENTLY IF EXISTS idx_access_control_policies_type_id;

이 마이그레이션은 threadmemberships 테이블에서 고아 행, 구체적으로는 연결된 사용자가 해당 스레드가 속한 채널의 멤버가 더 이상 아닌 행을 제거합니다. 정리는 threadmemberships, threads, channelmembers 세 테이블을 조인한 필터링된 DELETE로 수행됩니다. 테이블, 컬럼, 인덱스 등 스키마 객체가 추가되거나 제거되지는 않으며 데이터 행만 영향을 받습니다. 대규모 인스턴스 참고 사항: 이 마이그레이션은 threads 및 channelmembers와의 조인을 포함해 threadmemberships 전체를 스캔하는 단일 비배치 삭제를 실행합니다. 행이 수천만 개에 이르거나 채널 멤버십 변동 이력이 많은 데이터베이스에서는 이 쿼리가 오래 실행되어 threadmemberships의 autovacuum을 차단하고 WAL 부하를 높이며 복제 지연을 유발할 수 있습니다. 대규모 배포 환경의 관리자는 업그레이드 전에 대표 데이터셋에서 실행 시간을 검증하고, 트래픽이 적은 시간대에 업그레이드를 계획하는 것이 좋습니다. 삭제된 행은 복구할 수 없으므로 다운 마이그레이션에는 롤백 SQL이 없습니다. 이 마이그레이션은 되돌릴 수 없습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- Drop ThreadMembership rows whose user is no longer a member of the thread's channel.
DELETE FROM threadmemberships WHERE (postid, userid) IN (
    SELECT
        threadmemberships.postid,
        threadmemberships.userid
    FROM
        threadmemberships
        JOIN threads ON threads.postid = threadmemberships.postid
        LEFT JOIN channelmembers ON channelmembers.userid = threadmemberships.userid
            AND threads.channelid = channelmembers.channelid
    WHERE
        channelmembers.channelid IS NULL
);

마이그레이션 000199는 rank 타입 속성 필드를 지원하기 위해 PostgreSQL의 property_field_type 열거형에 새 값 'rank'를 추가합니다. 이 변경은 ALTER TYPE property_field_type ADD VALUE IF NOT EXISTS 'rank'로 이루어지며, pg_enum 시스템 카탈로그만 건드리는 카탈로그 전용 작업이므로 PropertyFields나 PropertyValues 같은 사용자 테이블에는 잠금을 걸지 않습니다. 기존 행과 애플리케이션 코드는 이 추가로 전혀 영향을 받지 않습니다. 롤백(다운) 마이그레이션은 의도적으로 파괴적입니다. Type = 'rank'인 PropertyFields 행은 'select'로 강제 변환되고, 열거형은 'rank' 값 없이 재생성되며, AttributeView 구체화 뷰가 삭제 후 재생성됩니다. 이 모든 작업은 PropertyFields에 대한 AccessExclusiveLock을 필요로 하므로 반드시 유지 보수 시간대에만 실행해야 합니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- Postgres cannot remove a value from an existing enum in place, so rebuild
-- the type without 'rank'. Any rows still holding 'rank' (e.g. fields created
-- via the property API after this migration ran) are coerced to 'select'
-- first so the recreated enum can accept them; 'select' has the same storage
-- shape (single option ID).
--
-- The AttributeView materialized view reads PropertyFields.Type, so Postgres
-- refuses to rebuild the enum on that column while the view exists. Drop the
-- view before the ALTER and recreate it afterwards.

UPDATE PropertyFields SET Type = 'select' WHERE Type = 'rank';

DROP MATERIALIZED VIEW IF EXISTS AttributeView;

ALTER TYPE property_field_type RENAME TO property_field_type_old;

CREATE TYPE property_field_type AS ENUM (
    'text',
    'select',
    'multiselect',
    'date',
    'user',
    'multiuser'
);

ALTER TABLE PropertyFields
    ALTER COLUMN Type TYPE property_field_type USING Type::text::property_field_type;

DROP TYPE property_field_type_old;

-- Recreate the no-rank AttributeView (matches 000177 / 000198 down).
CREATE MATERIALIZED VIEW IF NOT EXISTS AttributeView AS
SELECT
    pv.GroupID,
    pv.TargetID,
    pv.TargetType,
    jsonb_object_agg(
        pf.Name,
        CASE
            WHEN pf.Type = 'select' THEN (
                SELECT to_jsonb(options.name)
                FROM jsonb_to_recordset(pf.Attrs->'options') AS options(id text, name text)
                WHERE options.id = pv.Value #>> '{}'
                LIMIT 1
            )
            WHEN pf.Type = 'multiselect' AND jsonb_typeof(pv.Value) = 'array' THEN (
                SELECT jsonb_agg(option_names.name)
                FROM jsonb_array_elements_text(pv.Value) AS option_id
                JOIN jsonb_to_recordset(pf.Attrs->'options') AS option_names(id text, name text)
                ON option_id = option_names.id
            )
            ELSE pv.Value
        END
    ) AS Attributes
FROM PropertyValues pv
LEFT JOIN PropertyFields pf ON pf.ID = pv.FieldID
WHERE (pv.DeleteAt = 0 OR pv.DeleteAt IS NULL)
  AND (pf.DeleteAt = 0 OR pf.DeleteAt IS NULL)
  AND pf.ObjectType = 'user'
GROUP BY pv.GroupID, pv.TargetID, pv.TargetType;

마이그레이션 000195는 access_control 속성 그룹에 속한 PropertyFields 테이블의 행을 최대 3개까지 갱신하여 Type 값을 'select'에서 'rank'로 변환하고, Attrs JSONB 컬럼에 저장된 options 배열의 각 요소에 rank 정수 키를 백필합니다. 영향을 받는 필드는 classification(ObjectType = 'template'), system_classification(ObjectType = 'system'), channel_classification(ObjectType = 'channel')이며, 모두 기능 플래그로 제어되는 분류 표시(classification-markings) 기능의 일부입니다. 스키마 구조 변경(DDL)은 없으며, 대상이 지정된 DML UPDATE 문만 실행되어 서버 규모와 관계없이 최대 3개 행만 건드립니다. 두 문 모두 단일 트랜잭션 안에서 실행되므로 중간 상태가 다른 세션에 노출되지 않습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- up migration

    -- Convert the classification-markings property fields from 'select' to 'rank'.
    -- Each (Name, ObjectType) pair is matched explicitly so unrelated fields that
    -- happen to share a name cannot be touched. At most three rows are updated.
    UPDATE PropertyFields
    SET Type = 'rank'
    WHERE Type = 'select'
      AND GroupID = (SELECT ID FROM PropertyGroups WHERE Name = 'access_control')
      AND (
            (Name = 'classification'         AND ObjectType = 'template')
         OR (Name = 'system_classification'  AND ObjectType = 'system')
         OR (Name = 'channel_classification' AND ObjectType = 'channel')
      );

    -- Backfill a rank onto every option.
    UPDATE PropertyFields pf
    SET Attrs = jsonb_set(
            pf.Attrs,
            '{options}',
            (
                SELECT jsonb_agg(elem.opt || jsonb_build_object('rank', elem.ord) ORDER BY elem.ord)
                FROM jsonb_array_elements(pf.Attrs->'options') WITH ORDINALITY AS elem(opt, ord)
            )
        )
    WHERE pf.Type = 'rank'
      AND pf.GroupID = (SELECT ID FROM PropertyGroups WHERE Name = 'access_control')
      AND (
            (pf.Name = 'classification'         AND pf.ObjectType = 'template')
         OR (pf.Name = 'system_classification'  AND pf.ObjectType = 'system')
         OR (pf.Name = 'channel_classification' AND pf.ObjectType = 'channel')
      )
      AND jsonb_typeof(pf.Attrs->'options') = 'array'
      AND jsonb_array_length(pf.Attrs->'options') > 0;
-- down migration

UPDATE PropertyFields pf
SET Attrs = jsonb_set(
        pf.Attrs,
        '{options}',
        (
            SELECT jsonb_agg(elem.opt - 'rank' ORDER BY elem.ord)
            FROM jsonb_array_elements(pf.Attrs->'options') WITH ORDINALITY AS elem(opt, ord)
        )
    )
WHERE pf.Type = 'rank'
  AND pf.GroupID = (SELECT ID FROM PropertyGroups WHERE Name = 'access_control')
  AND (
        (pf.Name = 'classification'         AND pf.ObjectType = 'template')
     OR (pf.Name = 'system_classification'  AND pf.ObjectType = 'system')
     OR (pf.Name = 'channel_classification' AND pf.ObjectType = 'channel')
  )
  AND jsonb_typeof(pf.Attrs->'options') = 'array'
  AND jsonb_array_length(pf.Attrs->'options') > 0;

UPDATE PropertyFields
SET Type = 'select'
WHERE Type = 'rank'
  AND GroupID = (SELECT ID FROM PropertyGroups WHERE Name = 'access_control')
  AND (
        (Name = 'classification'         AND ObjectType = 'template')
     OR (Name = 'system_classification'  AND ObjectType = 'system')
     OR (Name = 'channel_classification' AND ObjectType = 'channel')
  );

마이그레이션 000196은 액세스 제어/속성 프레임워크의 일부로 PropertyFields 테이블 내 데이터를 갱신합니다. 구체적으로는 access_control PropertyGroups 그룹 안의 분류 필드 항목 두 개, 즉 system_classification 항목(ObjectType = 'system')과 channel_classification 항목(ObjectType = 'channel')의 이름을 모두 표준 값인 classification으로 변경합니다. PropertyFields의 고유 인덱스가 ObjectType을 포함하므로 두 행은 충돌 없이 구분된 상태로 남습니다. 이 변경은 최대 2개 행에만 영향을 주며 거의 즉시 완료되어 성능에 미치는 영향은 측정되지 않는 수준입니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- Up migration
-- Rename the system- and channel-scoped classification linked fields so all
-- three classification fields share the canonical Name 'classification'. They
-- remain distinct rows because the typed unique index keys on ObjectType as
-- well, so template/system/channel rows do not collide.
UPDATE PropertyFields
SET Name = 'classification'
WHERE GroupID = (SELECT ID FROM PropertyGroups WHERE Name = 'access_control')
  AND (
        (Name = 'system_classification'  AND ObjectType = 'system')
     OR (Name = 'channel_classification' AND ObjectType = 'channel')
  );

-- Down migration
-- Restore the previous names so the rows can be matched by 000195's down
-- migration. Identified by ObjectType since all three classification rows
-- share the same Name='classification' at this point.
UPDATE PropertyFields
SET Name = 'system_classification'
WHERE GroupID = (SELECT ID FROM PropertyGroups WHERE Name = 'access_control')
  AND Name = 'classification'
  AND ObjectType = 'system';

UPDATE PropertyFields
SET Name = 'channel_classification'
WHERE GroupID = (SELECT ID FROM PropertyGroups WHERE Name = 'access_control')
  AND Name = 'classification'
  AND ObjectType = 'channel';

마이그레이션 000178은 ABAC/속성 시스템의 일부인 AttributeView 구체화 뷰를 확장해 rank 타입 속성 필드를 지원합니다. 이전에는 rank 타입 값이 뷰에 원시 값으로 저장되었지만, 이제 {"name": <option-name>, "rank": <option-rank>} 형태의 JSON 객체로 구체화되어 ABAC 정책이 rank 정수를 정책 표현식에 직접 넣지 않고도 rank 값을 비교할 수 있습니다. 이 마이그레이션은 단일 트랜잭션 안에서 AttributeView를 삭제 후 재생성하며, 그 과정에서 PropertyValues와 PropertyFields를 전체 스캔합니다. 두 테이블은 기능 플래그로 제어되며 이 마이그레이션 시점에는 비어 있거나 거의 비어 있을 것으로 예상되므로, 대부분의 배포 환경에서 성능 영향은 무시할 수 있는 수준입니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- Extend the AttributeView matview to materialize rank-typed property values
-- as {"name": <option-name>, "rank": <option-rank>} so ABAC policies can
-- compare ranks without storing rank integers in policy expressions.
DROP MATERIALIZED VIEW IF EXISTS AttributeView;

CREATE MATERIALIZED VIEW IF NOT EXISTS AttributeView AS
SELECT
    pv.GroupID,
    pv.TargetID,
    pv.TargetType,
    jsonb_object_agg(
        pf.Name,
        CASE
            WHEN pf.Type = 'select' THEN (
                SELECT to_jsonb(options.name)
                FROM jsonb_to_recordset(pf.Attrs->'options') AS options(id text, name text)
                WHERE options.id = pv.Value #>> '{}'
                LIMIT 1
            )
            WHEN pf.Type = 'multiselect' AND jsonb_typeof(pv.Value) = 'array' THEN (
                SELECT jsonb_agg(option_names.name)
                FROM jsonb_array_elements_text(pv.Value) AS option_id
                JOIN jsonb_to_recordset(pf.Attrs->'options') AS option_names(id text, name text)
                ON option_id = option_names.id
            )
            WHEN pf.Type = 'rank' THEN (
                SELECT jsonb_build_object(
                    'name', options.name,
                    'rank', options.rank
                )
                FROM jsonb_to_recordset(pf.Attrs->'options')
                     AS options(id text, name text, rank int)
                WHERE options.id = pv.Value #>> '{}'
                LIMIT 1
            )
            ELSE pv.Value
        END
    ) AS Attributes
FROM PropertyValues pv
LEFT JOIN PropertyFields pf ON pf.ID = pv.FieldID
WHERE (pv.DeleteAt = 0 OR pv.DeleteAt IS NULL)
  AND (pf.DeleteAt = 0 OR pf.DeleteAt IS NULL)
  AND pf.ObjectType = 'user'
GROUP BY pv.GroupID, pv.TargetID, pv.TargetType;
-- Restore the 000177 matview definition (no rank branch).
DROP MATERIALIZED VIEW IF EXISTS AttributeView;

CREATE MATERIALIZED VIEW IF NOT EXISTS AttributeView AS
SELECT
    pv.GroupID,
    pv.TargetID,
    pv.TargetType,
    jsonb_object_agg(
        pf.Name,
        CASE
            WHEN pf.Type = 'select' THEN (
                SELECT to_jsonb(options.name)
                FROM jsonb_to_recordset(pf.Attrs->'options') AS options(id text, name text)
                WHERE options.id = pv.Value #>> '{}'
                LIMIT 1
            )
            WHEN pf.Type = 'multiselect' AND jsonb_typeof(pv.Value) = 'array' THEN (
                SELECT jsonb_agg(option_names.name)
                FROM jsonb_array_elements_text(pv.Value) AS option_id
                JOIN jsonb_to_recordset(pf.Attrs->'options') AS option_names(id text, name text)
                ON option_id = option_names.id
            )
            ELSE pv.Value
        END
    ) AS Attributes
FROM PropertyValues pv
LEFT JOIN PropertyFields pf ON pf.ID = pv.FieldID
WHERE (pv.DeleteAt = 0 OR pv.DeleteAt IS NULL)
  AND (pf.DeleteAt = 0 OR pf.DeleteAt IS NULL)
  AND pf.ObjectType = 'user'
GROUP BY pv.GroupID, pv.TargetID, pv.TargetType;

PAT 만료 적용을 지원하기 위해 useraccesstokens에 expiresat 컬럼과 부분 인덱스를 추가했습니다. useraccesstokens는 큰 테이블이 아니므로 이 마이그레이션에는 대용량 데이터셋 테스트가 필요하지 않았습니다. useraccesstokens는 컬럼 추가 시 짧은 ACCESS EXCLUSIVE 잠금(마이그레이션 000187, 메타데이터 전용이며 PostgreSQL 11 이상에서 테이블 재작성 없음)을 받고, CREATE INDEX CONCURRENTLY를 통한 인덱스 빌드 중에는 SHARE UPDATE EXCLUSIVE 잠금(마이그레이션 000188)을 받는데, 후자는 동시 읽기나 쓰기를 결코 차단하지 않습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- Migration 000187 (transactional):
ALTER TABLE useraccesstokens ADD COLUMN IF NOT EXISTS expiresat bigint NOT NULL DEFAULT 0;

-- Migration 000188 (run via morph as nontransactional):
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_useraccesstokens_expiresat
    ON useraccesstokens (expiresat)
    WHERE expiresat > 0;

-- Down migrations:
DROP INDEX CONCURRENTLY IF EXISTS idx_useraccesstokens_expiresat;
ALTER TABLE useraccesstokens DROP COLUMN IF EXISTS expiresat;

channelbookmarks 테이블에 targetid 컬럼과 그에 대응하는 인덱스를 도입해 새 채널 북마크 타입인 board를 지원합니다. 변경되는 테이블은 channelbookmarks 하나뿐입니다. 널 허용 컬럼 추가는 메타데이터 전용이며 배타 잠금 시간도 무시할 수 있는 수준입니다. 인덱스 빌드는 CONCURRENTLY를 사용해 DML을 차단하지 않고 온라인으로 실행됩니다. 기존 link와 file 북마크 행 및 쿼리는 영향을 받지 않으며, 새 targetid 컬럼은 기본값 없이 널을 허용하므로 구버전 코드에 아무런 영향을 주지 않습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- 000190_channel_bookmarks_board_target_id.up.sql
ALTER TYPE channel_bookmark_type ADD VALUE IF NOT EXISTS 'board';
ALTER TABLE channelbookmarks ADD COLUMN IF NOT EXISTS targetid varchar(26) DEFAULT NULL;

-- 000191_channel_bookmarks_target_id_index.up.sql
-- morph:nontransactional
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_channelbookmarks_type_targetid ON channelbookmarks (type, targetid) WHERE targetid IS NOT NULL;

PropertyGroups 테이블에 SchemaVersion 컬럼(integer DEFAULT 1 NOT NULL)을 추가했습니다. 이 마이그레이션에는 대용량 데이터셋 테스트를 수행하지 않았습니다. 변경되는 테이블은 등록된 속성 그룹마다 한 행씩 보유하는 작은 레지스트리 테이블인 PropertyGroups 하나뿐입니다. PropertyGroups는 컬럼 추가 시 짧은 access exclusive 잠금을 받지만, 테이블 재작성 없이 메타데이터 전용 카탈로그 갱신을 위해 잠깐만 유지됩니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

ALTER TABLE PropertyGroups ADD COLUMN IF NOT EXISTS SchemaVersion integer DEFAULT 1 NOT NULL;

새 데이터베이스 마이그레이션 두 개가 ChannelGuards 테이블을 도입합니다. 이 테이블을 통해 플러그인이 채널에 대한 배타적 소유권을 등록할 수 있습니다. 마이그레이션 000185는 ChannelId, PluginId, CreatedAt 세 컬럼과 채널별·플러그인별 고유성을 보장하는 (ChannelId, PluginId) 복합 기본 키를 가진 ChannelGuards 테이블을 생성합니다. 마이그레이션 000186은 플러그인 범위 쿼리를 효율적으로 처리하기 위해 PluginId 컬럼에 보조 인덱스 idx_channel_guards_plugin_id를 추가합니다. 두 마이그레이션 모두 온라인으로 안전하게 실행되며 기존 운영 테이블에 잠금 영향을 주지 않습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- 000185_create_channel_guards.up.sql
CREATE TABLE IF NOT EXISTS ChannelGuards (
    ChannelId varchar(26)  NOT NULL,
    PluginId  varchar(190) NOT NULL,
    CreatedAt bigint       NOT NULL,
    PRIMARY KEY (ChannelId, PluginId)
);
-- 000185_create_channel_guards.down.sql
DROP TABLE IF EXISTS ChannelGuards;
-- 000186_create_channel_guards_plugin_id_index.up.sql
-- morph:nontransactional
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_channel_guards_plugin_id ON ChannelGuards(PluginId);
-- 000186_create_channel_guards_plugin_id_index.down.sql
-- morph:nontransactional
DROP INDEX CONCURRENTLY IF EXISTS idx_channel_guards_plugin_id;
v11.8

Custom Profile Attributes 속성 그룹의 이름이 custom_profile_attributes에서 access_control로 변경되고, CPA 필드와 값이 레거시 속성 모델에서 v2 모델로 마이그레이션됩니다. CPA 기능의 동작 자체는 변경되지 않습니다. CPA를 사용하는 플러그인 개발자는 새 그룹 이름으로 등록해야 합니다.

PSAv1(레거시) 그룹과 PSAv2 그룹을 구분하기 위해 PropertyGroups 테이블에 새 Version 컬럼을 추가했습니다. 기존 그룹은 기본값 1을 갖게 되어 모든 레거시 호출자의 현재 동작이 유지됩니다. PropertyGroups는 설정 성격의 작은 테이블이며, ADD COLUMN ... DEFAULT 1 NOT NULL은 PostgreSQL에서 메타데이터 전용 작업이므로 테이블 재작성이 발생하지 않고 PropertyGroups에 대한 AccessExclusiveLock도 밀리초 단위로만 유지됩니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- Pre-upgrade schema changes (Mattermost 11.8).

 ALTER TABLE PropertyGroups ADD COLUMN IF NOT EXISTS Version integer DEFAULT 1 NOT NULL;

posts.rootid와 posts.channelid에 대한 PostgreSQL 플래너 통계 목표치를 기본값 100에서 5000으로 상향하고, ANALYZE posts (rootid, channelid)로 플래너 통계를 갱신합니다. ALTER COLUMN ... SET STATISTICS 문은 SHARE UPDATE EXCLUSIVE 잠금만 획득하는 메타데이터 전용 작업이므로 테이블 재작성이 발생하지 않고 posts에 대한 동시 SELECT, INSERT, UPDATE, DELETE도 차단되지 않습니다. ANALYZE 단계는 표본 크기가 커진 탓에 대규모 posts 테이블(1억 행 이상)에서는 수 분이 걸릴 수 있지만 읽기나 쓰기를 차단하지는 않습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

ALTER TABLE posts ALTER COLUMN rootid SET STATISTICS 5000;
ALTER TABLE posts ALTER COLUMN channelid SET STATISTICS 5000;
ANALYZE posts (rootid, channelid);

channel_type 열거형에 새 값 'BO'와 'BP'를 추가했습니다. ALTER TYPE ... ADD VALUE 문은 channel_type 타입에만 짧은 AccessExclusiveLock을 획득하며, 테이블 재작성이 발생하지 않고 channels 테이블에 행 수준 잠금도 적용되지 않으므로 작업이 거의 즉시 끝납니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

ALTER TYPE channel_type ADD VALUE IF NOT EXISTS 'BO';
ALTER TYPE channel_type ADD VALUE IF NOT EXISTS 'BP';

같은 릴리스 주기에 속성 및 어트리뷰트 시스템(PropertyFields, PropertyGroups, AttributeView)에 스키마 마이그레이션 네 개를 도입했습니다. 마이그레이션 000168과 000169는 PropertyFields에 널 허용 컬럼 LinkedFieldID varchar(26)와 그에 대한 부분 동시 인덱스를 추가해 한 필드가 다른 필드를 링크로 참조할 수 있게 합니다. 마이그레이션 000176과 000177은 PropertyFields 행의 권한 컬럼을 갱신하고, PropertyGroups 항목의 이름을 custom_profile_attributes에서 access_control로 변경하며, AttributeView 구체화 뷰를 사용자 범위 어트리뷰트로만 좁혀 기존 Custom Profile Attributes 속성 그룹을 보다 범용적인 access_control 그룹으로 승격합니다. 마이그레이션 000176과 000177은 반드시 함께 배포해야 합니다. 영향을 받는 테이블 중 대형 테이블 목록에 포함된 것은 없습니다. 속성 시스템 테이블은 크기가 작습니다. 참고: 롤링 업그레이드 구간 동안 그룹 조회 키로 'custom_profile_attributes'를 하드코딩한 구 ESR 노드는 CPA 프로필 읽기/쓰기 엔드포인트와 ABAC 정책 관리 관리자 UI에서 일시적인 실패를 겪습니다. 최종 사용자의 채널 접근과 멤버십 동기화 워커는 영향을 받지 않습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- 000168: Add LinkedFieldID column
ALTER TABLE PropertyFields ADD COLUMN IF NOT EXISTS LinkedFieldID varchar(26);

-- 000169: Create partial index on LinkedFieldID
CREATE INDEX CONCURRENTLY idx_propertyfields_linkedfieldid ON PropertyFields(LinkedFieldID) WHERE LinkedFieldID IS NOT NULL AND DeleteAt = 0;

-- 000176: Migrate CPA to access_control
UPDATE PropertyFields SET ObjectType = 'user' WHERE GroupID = (SELECT Id FROM PropertyGroups WHERE Name = 'custom_profile_attributes');
UPDATE PropertyGroups SET Name = 'access_control', Version = 2 WHERE Name = 'custom_profile_attributes';

-- 000177: Refresh AttributeView with user-scoped filter
DROP MATERIALIZED VIEW AttributeView;
CREATE MATERIALIZED VIEW AttributeView ... WHERE pf.ObjectType = 'user';

Mattermost v11.8은 Recaps 테이블에 새 ViewedAt 컬럼과 (UserId, ViewedAt) 인덱스를 추가하는 스키마 변경을 도입합니다. 컬럼은 메타데이터 전용 작업으로 추가되고 인덱스는 CONCURRENTLY로 생성되므로, 테이블 잠금이 발생하지 않고 테이블에 대한 기존 작업도 영향을 받지 않습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- 000172_add_recaps_viewed_at.up.sql
ALTER TABLE Recaps ADD COLUMN IF NOT EXISTS ViewedAt BIGINT NOT NULL DEFAULT 0;

-- 000172_add_recaps_viewed_at.down.sql
ALTER TABLE Recaps DROP COLUMN IF EXISTS ViewedAt;

-- 000173_create_recaps_user_id_viewed_at_index.up.sql
-- morph:nontransactional
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_recaps_user_id_viewed_at ON Recaps(UserId, ViewedAt);

-- 000173_create_recaps_user_id_viewed_at_index.down.sql
-- morph:nontransactional
DROP INDEX CONCURRENTLY IF EXISTS idx_recaps_user_id_viewed_at;

Mattermost v11.8은 새로운 채널 참여 요청 워크플로와 검색 가능한 비공개 채널을 지원하는 스키마 변경 여섯 가지를 도입합니다. Channels 테이블에 Discoverable 불리언 컬럼(DEFAULT FALSE)이 메타데이터 전용 작업으로 추가되며 PostgreSQL 11 이상에서 즉시 완료됩니다. 접근 요청 워크플로를 관리하기 위해 요청 상태, 거부 사유, 검토자 정보를 추적하는 새 ChannelJoinRequests 테이블이 생성됩니다. 이어서 동시 인덱스 네 개가 추가됩니다. 검색 가능한 팀 채널을 위한 Channels의 부분 인덱스(idx_channels_discoverable_team), 대기 중인 요청을 위한 ChannelJoinRequests의 고유 부분 인덱스(idx_channeljoinrequests_pending_unique), 그리고 채널+상태와 사용자+상태 기준 쿼리를 각각 최적화하는 복합 인덱스 두 개입니다. 모든 인덱스 작업은 CONCURRENTLY를 사용하므로 명시적 트랜잭션 블록 밖에서 실행해야 합니다. 이 마이그레이션은 PostgreSQL 전용입니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- Migration 000178: Add Discoverable column
ALTER TABLE Channels ADD COLUMN IF NOT EXISTS Discoverable BOOLEAN NOT NULL DEFAULT FALSE;
-- Migration 000179: Partial index for discoverable channels
-- Run outside a transaction block.
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_channels_discoverable_team
    ON Channels (TeamId)
    WHERE Discoverable = TRUE AND Type = 'P' AND DeleteAt = 0;
-- Migration 000180: Create ChannelJoinRequests table
CREATE TABLE IF NOT EXISTS ChannelJoinRequests (
    Id           VARCHAR(26)  PRIMARY KEY,
    ChannelId    VARCHAR(26)  NOT NULL,
    UserId       VARCHAR(26)  NOT NULL,
    Message      TEXT         NOT NULL DEFAULT '',
    Status       VARCHAR(16)  NOT NULL DEFAULT 'pending',
    DenialReason TEXT         NOT NULL DEFAULT '',
    CreateAt     BIGINT       NOT NULL,
    UpdateAt     BIGINT       NOT NULL,
    ReviewedBy   VARCHAR(26)  NOT NULL DEFAULT '',
    ReviewedAt   BIGINT       NOT NULL DEFAULT 0
);
-- Migration 000181: Unique index for pending requests
-- Run outside a transaction block.
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS idx_channeljoinrequests_pending_unique
    ON ChannelJoinRequests (ChannelId, UserId)
    WHERE Status = 'pending';
-- Migration 000182: Index for channel+status queries
-- Run outside a transaction block.
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_channeljoinrequests_channel_status_createat
    ON ChannelJoinRequests (ChannelId, Status, CreateAt DESC);
-- Migration 000183: Index for user+status queries
-- Run outside a transaction block.
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_channeljoinrequests_user_status_createat
    ON ChannelJoinRequests (UserId, Status, CreateAt DESC);

Mattermost v11.8은 PostgreSQL permission_level 열거형 타입을 확장해 기존 none, sysadmin, member 값에 admin 값을 추가합니다. 이는 순수한 카탈로그 변경으로, — ALTER TYPE ADD VALUE는 pg_type 시스템 카탈로그만 갱신하며 PropertyFields 테이블이나 다른 애플리케이션 테이블은 건드리지 않습니다. 업 마이그레이션으로 수정되는 컬럼, 인덱스, 테이블 데이터는 없습니다. 이 변경으로 속성 필드 액세스 제어 모델에 새로운 admin 수준 권한 계층을 사용할 수 있게 됩니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- 000189_add_admin_to_permission_level.up.sql
ALTER TYPE permission_level ADD VALUE IF NOT EXISTS 'admin';
v11.7

FIPS 빌드는 비밀번호, atmos/camo 프록시 설정, 공유 채널 시크릿에 최소 14자를 요구합니다. 기존 사용자의 더 짧은 비밀번호는 더 이상 유효하지 않으며 비밀번호 재설정이 필요합니다. 비 FIPS 빌드는 영향을 받지 않습니다.

AccessControlPolicies 테이블의 (Name, Type)에 WHERE Type = 'parent'로 필터링된 새 부분 고유 인덱스(idx_accesscontrolpolicies_name_type)를 추가했습니다. 인덱스를 생성하기 전에 중복된 상위 정책 이름은 가장 오래된 항목을 제외한 나머지에 정책 ID를 덧붙여 해소됩니다. 이로써 상위 정책 이름의 고유성이 강제되고 이후 중복이 생기는 것도 방지됩니다. 마이그레이션은 1초 미만이 걸립니다. 이 마이그레이션의 대상은 posts나 reactions가 아니라 AccessControlPolicies입니다. AttributeBasedAccessControl이 활성화되지 않은 배포 환경에서는 테이블이 비어 있으므로 마이그레이션이 1초 이내에 완료됩니다. 플래그가 활성화된 배포 환경에서도 상위 정책 수가 적을 것으로 예상되므로 소요 시간은 여전히 무시할 수준입니다. CREATE UNIQUE INDEX(비동시 방식)는 AccessControlPolicies에 SHARE 잠금을 걸어 인덱스 빌드 동안 동시 INSERT/UPDATE/DELETE를 차단합니다. 중복 제거를 수행하는 UPDATE는 같은 테이블에 ROW EXCLUSIVE 잠금을 겁니다. 대부분의 자체 호스팅 배포 환경에서는 AttributeBasedAccessControl 기능 플래그가 활성화되어 있지 않았을 것이므로 테이블이 비어 있고 두 작업 모두 밀리초 단위로 완료됩니다. 기능 플래그가 활성화된 Cloud 또는 얼리 액세스 배포 환경에서는 테이블에 데이터가 있을 수 있지만 상위 정책 수가 적을 것으로 예상되므로 잠금 구간은 짧게 유지됩니다. CREATE UNIQUE INDEX(비동시 방식)는 AccessControlPolicies에 짧은 SHARE 잠금을 겁니다. AttributeBasedAccessControl이 활성화되지 않은 배포 환경에서는 테이블이 비어 있어 잠금이 무시할 수준입니다. 플래그가 활성화된 배포 환경에서도 상위 정책 수가 적을 것으로 예상되므로 어느 경우든 잠금 구간은 짧게 유지됩니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- PostgreSQL only (MySQL not supported in v11+):
  -- Deduplicate parent policy names before adding unique constraint.
  -- The oldest policy (by CreateAt) keeps its original name; duplicates get ' (<id>)' appended.
  UPDATE AccessControlPolicies AS p
  SET Name = LEFT(p.Name, 128 - LENGTH(' (' || p.ID || ')')) || ' (' || p.ID || ')'
  FROM (
      SELECT ID, Name, ROW_NUMBER() OVER (PARTITION BY Name ORDER BY CreateAt ASC) AS rn
      FROM AccessControlPolicies
      WHERE Type = 'parent'
  ) AS dupes
  WHERE p.ID = dupes.ID
    AND dupes.rn > 1;

  CREATE UNIQUE INDEX IF NOT EXISTS idx_accesscontrolpolicies_name_type
    ON AccessControlPolicies (Name, Type)
    WHERE Type = 'parent';

PropertyFields 테이블이 변경됩니다. 각 필드를 생성하고 마지막으로 수정한 사용자를 추적하기 위해 널 허용 컬럼 CreatedBy와 UpdatedBy가 추가됩니다. 레거시 속성 필드와 타입이 지정된 속성 필드를 구분하기 위해 NOT NULL 제약과 빈 문자열 기본값을 가진 새 ObjectType 컬럼이 추가됩니다. 기본값이 FALSE인 새 Protected 불리언 컬럼과 함께, none·sysadmin·member 값을 갖는 새 permission_level 열거형 타입을 사용하는 컬럼 세 개(PermissionField, PermissionValues, PermissionOptions)가 추가됩니다. (GroupID, TargetID, Name)에 대한 기존 고유 인덱스 idx_propertyfields_unique는 삭제되고, 새 부분 고유 인덱스 두 개로 대체됩니다. ObjectType = ''인 레거시 행을 포괄하는 idx_propertyfields_unique_legacy와, ObjectType != ''인 타입 지정 행을 포괄하며 (ObjectType, GroupID, TargetType, TargetID, Name)을 인덱싱하는 idx_propertyfields_unique_typed입니다.

PropertyValues 테이블이 변경됩니다. PropertyFields의 동일한 추적 컬럼과 짝을 이루는 널 허용 컬럼 CreatedBy와 UpdatedBy가 추가됩니다.

새 Views 테이블이 생성됩니다. 이 테이블에 대해 (ChannelId, DeleteAt)에 새 인덱스 idx_views_channel_id_delete_at가 생성됩니다.

마이그레이션 160, 161, 165는 ALTER TABLE ADD COLUMN을 사용해 잠깐 잠금을 획득하지만, PostgreSQL 11 이상에서는 메타데이터 전용이라 즉시 끝납니다. 인덱스 생성과 삭제를 포함한 나머지 마이그레이션은 모두 CONCURRENTLY를 사용해 잠금이 없습니다. 모든 잠금은 즉시 끝나는 메타데이터 작업이고 모든 인덱스 작업은 CONCURRENTLY를 사용합니다. 이 여덟 개 마이그레이션은 무중단 배포에 안전합니다. 행 수가 적은 테이블(PropertyFields, PropertyValues)에만 영향을 주고 비어 있는 새 테이블(Views)을 생성하며, 전체 테이블 데이터 수정이나 컬럼 타입 변경이 없고 동시 인덱스 작업으로 인한 I/O도 최소한입니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

--- 160
ALTER TABLE PropertyFields
ADD COLUMN IF NOT EXISTS CreatedBy varchar(26),
ADD COLUMN IF NOT EXISTS UpdatedBy varchar(26);

ALTER TABLE PropertyValues
ADD COLUMN IF NOT EXISTS CreatedBy varchar(26),
ADD COLUMN IF NOT EXISTS UpdatedBy varchar(26);

--- 161
ALTER TABLE PropertyFields ADD COLUMN IF NOT EXISTS ObjectType varchar(255) NOT NULL DEFAULT '';

--- 162
DROP INDEX CONCURRENTLY IF EXISTS idx_propertyfields_unique;

--- 163
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS idx_propertyfields_unique_legacy
    ON PropertyFields (GroupID, TargetID, Name)
    WHERE DeleteAt = 0 AND ObjectType = '';

--- 164
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS idx_propertyfields_unique_typed
    ON PropertyFields (ObjectType, GroupID, TargetType, TargetID, Name)
    WHERE DeleteAt = 0 AND ObjectType != '';

--- 165
DO $$
BEGIN
  IF NOT EXISTS (SELECT * FROM pg_type typ
                            INNER JOIN pg_namespace nsp ON nsp.oid = typ.typnamespace
                        WHERE nsp.nspname = current_schema()
                            AND typ.typname = 'permission_level') THEN
    CREATE TYPE permission_level AS ENUM ('none', 'sysadmin', 'member');
  END IF;
END;
$$
LANGUAGE plpgsql;

ALTER TABLE PropertyFields
ADD COLUMN IF NOT EXISTS Protected BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS PermissionField permission_level,
ADD COLUMN IF NOT EXISTS PermissionValues permission_level,
ADD COLUMN IF NOT EXISTS PermissionOptions permission_level;

--- 166
CREATE TABLE IF NOT EXISTS Views (
    Id          VARCHAR(26)  PRIMARY KEY,
    ChannelId   VARCHAR(26)  NOT NULL,
    Type        VARCHAR(32)  NOT NULL,
    CreatorId   VARCHAR(26)  NOT NULL,
    Title       VARCHAR(256) NOT NULL,
    Description TEXT,
    SortOrder   INTEGER      NOT NULL DEFAULT 0,
    Props       jsonb,
    CreateAt    BIGINT       NOT NULL,
    UpdateAt    BIGINT       NOT NULL,
    DeleteAt    BIGINT       NOT NULL DEFAULT 0
);

--- 167
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_views_channel_id_delete_at ON Views(ChannelId, DeleteAt);

--- 171
DROP INDEX CONCURRENTLY IF EXISTS idx_propertyfields_protected;

role_updated WebSocket 이벤트가 전역 브로드캐스트 대신 영향을 받는 팀/채널로 범위가 좁혀져 성능이 개선되고 불필요한 네트워크 트래픽이 줄어듭니다. 마이그레이션은 업그레이드 중 자동으로 실행되므로 관리자가 수동으로 취해야 할 조치는 없습니다. 다운타임은 예상되지 않으며, 마이그레이션이 정상 운영 중에도 실행될 수 있으므로 업그레이드를 위한 별도 계획도 필요하지 않습니다. 모든 설치 환경이 이 변경의 영향을 받지만, roles 테이블은 규모가 큰 설치 환경에서도 1만 행에 훨씬 못 미치는 작은 테이블이므로 영향은 미미합니다. 이번 업그레이드에는 역할 스킴 조회를 효율화하기 위해 roles 테이블에 schemeid 컬럼을 추가하는 자동 데이터베이스 마이그레이션이 포함됩니다. 마이그레이션은 PostgreSQL의 비차단 기법을 사용합니다. 먼저 짧은 ACCESS EXCLUSIVE 잠금(카탈로그 전용 작업)으로 컬럼을 추가하고, 다음으로 ROW EXCLUSIVE 잠금으로 기존 데이터를 백필하며, 마지막으로 다른 작업을 차단하지 않는 SHARE UPDATE EXCLUSIVE 잠금으로 인덱스를 동시에 생성합니다. roles 테이블의 크기는 제한적이며 posts, reactions 등 대량 데이터의 영향을 받지 않습니다. 이 마이그레이션은 완전히 하위 호환됩니다.

v11.7에는 Agents 플러그인 v2가 포함됩니다. Mattermost Agents 플러그인을 v1.x 릴리스에서 v2.0.0으로 업그레이드하는 방법은 이 가이드를 참고하세요. 지원되는 버전 경로, v2.0.0 최초 기동 시 자동으로 실행되는 마이그레이션, 관리자가 업그레이드 작업 전에 알아야 할 호환성 파괴 변경과 기본 동작 변경, 그리고 업그레이드 성공을 확인하는 검증 절차를 다룹니다.

v11.7.6 마이그레이션은 threadmemberships 테이블에서 고아 행, 구체적으로는 연결된 사용자가 해당 스레드가 속한 채널의 멤버가 더 이상 아닌 행을 제거합니다. 정리는 threadmemberships, threads, channelmembers 세 테이블을 조인한 필터링된 DELETE로 수행됩니다. 테이블, 컬럼, 인덱스 등 스키마 객체가 추가되거나 제거되지는 않으며 데이터 행만 영향을 받습니다. 대규모 인스턴스 참고 사항: 이 마이그레이션은 threads 및 channelmembers와의 조인을 포함해 threadmemberships 전체를 스캔하는 단일 비배치 삭제를 실행합니다. 행이 수천만 개에 이르거나 채널 멤버십 변동 이력이 많은 데이터베이스에서는 이 쿼리가 오래 실행되어 threadmemberships의 autovacuum을 차단하고 WAL 부하를 높이며 복제 지연을 유발할 수 있습니다. 대규모 배포 환경의 관리자는 업그레이드 전에 대표 데이터셋에서 실행 시간을 검증하고, 트래픽이 적은 시간대에 업그레이드를 계획하는 것이 좋습니다. 삭제된 행은 복구할 수 없으므로 다운 마이그레이션에는 롤백 SQL이 없습니다. 이 마이그레이션은 되돌릴 수 없습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- Drop ThreadMembership rows whose user is no longer a member of the thread's channel.
DELETE FROM threadmemberships WHERE (postid, userid) IN (
    SELECT
        threadmemberships.postid,
        threadmemberships.userid
    FROM
        threadmemberships
        JOIN threads ON threads.postid = threadmemberships.postid
        LEFT JOIN channelmembers ON channelmembers.userid = threadmemberships.userid
            AND threads.channelid = channelmembers.channelid
    WHERE
        channelmembers.channelid IS NULL
);
v11.6

단일 채널 게스트는 더 이상 기본 라이선스 좌석 수에 포함되지 않으며, 라이선스 좌석과 1:1 비율까지 무료로 허용됩니다. 새로운 통계 카드와 라이선스 행, 관리자 배너를 통해 단일 채널 게스트 사용량과 초과 경고를 확인할 수 있습니다.

v11.6.2부터 role_updated WebSocket 이벤트가 전역 브로드캐스트 대신 영향을 받는 팀/채널로 범위가 좁혀져 성능이 개선되고 불필요한 네트워크 트래픽이 줄어듭니다. 마이그레이션은 업그레이드 중 자동으로 실행되므로 관리자가 수동으로 취해야 할 조치는 없습니다. 다운타임은 예상되지 않으며, 마이그레이션이 정상 운영 중에도 실행될 수 있으므로 업그레이드를 위한 별도 계획도 필요하지 않습니다. 모든 설치 환경이 이 변경의 영향을 받지만, roles 테이블은 규모가 큰 설치 환경에서도 1만 행에 훨씬 못 미치는 작은 테이블이므로 영향은 미미합니다. 이번 업그레이드에는 역할 스킴 조회를 효율화하기 위해 roles 테이블에 schemeid 컬럼을 추가하는 자동 데이터베이스 마이그레이션이 포함됩니다. 마이그레이션은 PostgreSQL의 비차단 기법을 사용합니다. 먼저 짧은 ACCESS EXCLUSIVE 잠금(카탈로그 전용 작업)으로 컬럼을 추가하고, 다음으로 ROW EXCLUSIVE 잠금으로 기존 데이터를 백필하며, 마지막으로 다른 작업을 차단하지 않는 SHARE UPDATE EXCLUSIVE 잠금으로 인덱스를 동시에 생성합니다. roles 테이블의 크기는 제한적이며 posts, reactions 등 대량 데이터의 영향을 받지 않습니다. 이 마이그레이션은 완전히 하위 호환됩니다.

v11.5

translations 테이블에 새 컬럼 translations.state와 새 인덱스 idx_translations_state를 추가했습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

ALTER TABLE translations
ADD COLUMN IF NOT EXISTS state varchar(20) NOT NULL;

CREATE INDEX IF NOT EXISTS idx_translations_state
ON translations(state)
WHERE state IN ('processing');

channelmembers 테이블에 새 컬럼 channelmembers.autotranslationdisabled를 추가했습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

ALTER TABLE channelmembers
    ADD COLUMN IF NOT EXISTS autotranslationdisabled boolean NOT NULL DEFAULT false;

translations 테이블에서 translations.objectType 컬럼을 수정하고 기본 키를 (objectId, dstLang)에서 (objectId, objectType, dstLang)으로 변경했습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

UPDATE translations SET objectType = 'post' WHERE objectType IS NULL;
ALTER TABLE translations ALTER COLUMN objectType SET NOT NULL;
ALTER TABLE translations DROP CONSTRAINT translations_pkey;
ALTER TABLE translations ADD PRIMARY KEY (objectId, objectType, dstLang);

translations 테이블에 새 컬럼 translations.channelid를 추가했습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

ALTER TABLE translations ADD COLUMN IF NOT EXISTS channelid varchar(26);

translations 테이블에 새 인덱스 idx_translations_channel_updateat를 추가했습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- morph:nontransactional
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_translations_channel_updateat
    ON translations(channelid, objectType, updateAt DESC, dstlang);

translations 테이블에서 인덱스 idx_translations_updateat를 삭제했습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- morph:nontransactional
DROP INDEX CONCURRENTLY IF EXISTS idx_translations_updateat;

v11.5.5부터 role_updated WebSocket 이벤트가 전역 브로드캐스트 대신 영향을 받는 팀/채널로 범위가 좁혀져 성능이 개선되고 불필요한 네트워크 트래픽이 줄어듭니다. 마이그레이션은 업그레이드 중 자동으로 실행되므로 관리자가 수동으로 취해야 할 조치는 없습니다. 다운타임은 예상되지 않으며, 마이그레이션이 정상 운영 중에도 실행될 수 있으므로 업그레이드를 위한 별도 계획도 필요하지 않습니다. 모든 설치 환경이 이 변경의 영향을 받지만, roles 테이블은 규모가 큰 설치 환경에서도 1만 행에 훨씬 못 미치는 작은 테이블이므로 영향은 미미합니다. 이번 업그레이드에는 역할 스킴 조회를 효율화하기 위해 roles 테이블에 schemeid 컬럼을 추가하는 자동 데이터베이스 마이그레이션이 포함됩니다. 마이그레이션은 PostgreSQL의 비차단 기법을 사용합니다. 먼저 짧은 ACCESS EXCLUSIVE 잠금(카탈로그 전용 작업)으로 컬럼을 추가하고, 다음으로 ROW EXCLUSIVE 잠금으로 기존 데이터를 백필하며, 마지막으로 다른 작업을 차단하지 않는 SHARE UPDATE EXCLUSIVE 잠금으로 인덱스를 동시에 생성합니다. roles 테이블의 크기는 제한적이며 posts, reactions 등 대량 데이터의 영향을 받지 않습니다. 이 마이그레이션은 완전히 하위 호환됩니다.

v11.4

Photoshop Document(PSD) 파일은 더 이상 인라인으로 미리 보기되지 않으며 일반 첨부 파일로 처리됩니다.

새 테이블 Recaps와 RecapChannels 두 개를 추가했습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- Recaps table: stores recap metadata
CREATE TABLE IF NOT EXISTS Recaps (
    Id VARCHAR(26) PRIMARY KEY,
    UserId VARCHAR(26) NOT NULL,
    Title VARCHAR(255) NOT NULL,
    CreateAt BIGINT NOT NULL,
    UpdateAt BIGINT NOT NULL,
    DeleteAt BIGINT NOT NULL,
    TotalMessageCount INT NOT NULL,
    Status VARCHAR(32) NOT NULL,
    ReadAt BIGINT DEFAULT 0 NOT NULL,
    BotID VARCHAR(26) DEFAULT '' NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_recaps_user_id ON Recaps(UserId);
CREATE INDEX IF NOT EXISTS idx_recaps_create_at ON Recaps(CreateAt);
CREATE INDEX IF NOT EXISTS idx_recaps_user_id_delete_at ON Recaps(UserId, DeleteAt);
CREATE INDEX IF NOT EXISTS idx_recaps_user_id_read_at ON Recaps(UserId, ReadAt);
CREATE INDEX IF NOT EXISTS idx_recaps_bot_id ON Recaps(BotID);

-- RecapChannels table: stores per-channel summaries
CREATE TABLE IF NOT EXISTS RecapChannels (
    Id VARCHAR(26) PRIMARY KEY,
    RecapId VARCHAR(26) NOT NULL,
    ChannelId VARCHAR(26) NOT NULL,
    ChannelName VARCHAR(64) NOT NULL,
    Highlights TEXT,
    ActionItems TEXT,
    SourcePostIds TEXT,
    CreateAt BIGINT NOT NULL,
    FOREIGN KEY (RecapId) REFERENCES Recaps(Id) ON DELETE CASCADE
);

CREATE INDEX IF NOT EXISTS idx_recap_channels_recap_id ON RecapChannels(RecapId);
CREATE INDEX IF NOT EXISTS idx_recap_channels_channel_id ON RecapChannels(ChannelId);
v11.3

v11.3.1부터 Photoshop Document(PSD) 파일은 더 이상 인라인으로 미리 보기되지 않으며 일반 첨부 파일로 처리됩니다.

사용자 어트리뷰트를 별도 테이블로 집계하는 새 테이블(ReadReceipts, TemporaryPosts) 형태의 스키마 변경을 추가했습니다. Drafts와 ScheduledPosts 양쪽에 Type 필드를 추가했습니다. 이전 버전의 Mattermost도 새 스키마 변경과 함께 동작할 수 있습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

CREATE TABLE IF NOT EXISTS ReadReceipts (
    PostId VARCHAR(26) NOT NULL,
    UserId VARCHAR(26) NOT NULL,
    ExpireAt bigint NOT NULL,
    PRIMARY KEY (PostId, UserId)
);

CREATE INDEX IF NOT EXISTS idx_read_receipts_post_id ON ReadReceipts(PostId);
CREATE INDEX IF NOT EXISTS idx_read_receipts_user_id_post_id_expire_at ON ReadReceipts(UserId, PostId, ExpireAt);

CREATE TABLE IF NOT EXISTS TemporaryPosts (
    PostId VARCHAR(26) PRIMARY KEY,
    Type VARCHAR(26) NOT NULL,
    ExpireAt BIGINT NOT NULL,
    Message VARCHAR(65535),
    FileIds VARCHAR(300)
);

CREATE INDEX IF NOT EXISTS idx_temporary_posts_expire_at ON TemporaryPosts(expireat);

ALTER TABLE drafts ADD COLUMN IF NOT EXISTS Type text;
ALTER TABLE scheduledposts ADD COLUMN IF NOT EXISTS Type text;

새 translations 테이블 하나, 새 컬럼 두 개(channels.autotranslation, channelmembers.autotranslation)), 새 인덱스 네 개를 추가했습니다. 이 마이그레이션은 완전히 하위 호환되며 테이블 재작성도 없습니다. 1초 미만의 다운타임이 필요합니다. 진정한 무중단 업그레이드를 원한다면 업그레이드 전에 CREATE INDEX CONCURRENTLY를 사용해 무중단으로 진행할 수 있습니다. 표준 업그레이드는 모든 인스턴스 규모에서 안전하며, 무중단이 반드시 필요한 경우가 아니라면 별도의 수동 절차는 필요하지 않습니다. 완전한 무중단을 위한 SQL 쿼리는 다음과 같습니다.

CREATE INDEX CONCURRENTLY IF NOT EXISTS
idx_channelmembers_autotranslation_enabled
   ON channelmembers (channelid)
   WHERE autotranslation = true;

CREATE INDEX CONCURRENTLY IF NOT EXISTS
idx_channels_autotranslation_enabled
   ON channels (id)
   WHERE autotranslation = true;

CREATE INDEX CONCURRENTLY IF NOT EXISTS
idx_users_id_locale
   ON users (id, locale);

Mattermost v11.3부터, registerRightHandSidebarComponent로 우측 사이드바(RHS) 컴포넌트를 등록하는 플러그인 중 RHS 컴포넌트가 플러그인 고유 상태에 의존하는 경우에는 RHS 팝아웃을 지원하기 위한 추가 코드를 구현해야 합니다. 자세한 내용은 이 포럼 게시글을 참고하세요.

v11.2

v11.2.3부터 Photoshop Document(PSD) 파일은 더 이상 인라인으로 미리 보기되지 않으며 일반 첨부 파일로 처리됩니다.

OAuthApps 테이블에 isdynamicallyregistered라는 새 컬럼을 추가했습니다. 기본값은 false입니다. 또한 OAuthAuthData 테이블에 resource, codechallenge, codechallengemethod 세 개의 새 컬럼을 추가했습니다. 모든 컬럼의 기본값은 ‘’입니다. 그리고 OAuthAccessData 테이블에 audience라는 새 컬럼을 추가했습니다. 기본값은 ‘’입니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 각 테이블에 ACCESS EXCLUSIVE LOCK이 걸리지만 메타데이터를 추가·제거하기 위한 것일 뿐이며 명령은 즉시 반환됩니다. 포함된 SQL 쿼리는 다음과 같습니다.

ALTER TABLE oauthapps ADD COLUMN IF NOT EXISTS isdynamicallyregistered BOOLEAN DEFAULT FALSE;

ALTER TABLE oauthauthdata ADD COLUMN IF NOT EXISTS codechallenge varchar(128) DEFAULT '';

ALTER TABLE oauthauthdata ADD COLUMN IF NOT EXISTS codechallengemethod varchar(10) DEFAULT '';

ALTER TABLE oauthaccessdata ADD COLUMN IF NOT EXISTS audience varchar(512) DEFAULT '';

ALTER TABLE oauthauthdata ADD COLUMN IF NOT EXISTS resource varchar(512) DEFAULT '';
v11.1

Mattermost 웹 앱이 사용하는 React 버전이 React 17에서 React 18로 갱신되었습니다. 자세한 내용은 이 포럼 게시글을 참고하세요.

Data Spillage 설정을 저장하기 위해 새 테이블 ContentFlaggingCommonReviewers, ContentFlaggingTeamSettings, ContentFlaggingTeamReviewers 세 개를 추가했습니다. 팀 설정 조회를 최적화하기 위해 ContentFlaggingTeamReviewers 테이블에 인덱스를 추가했습니다. 이 마이그레이션은 완전히 하위 호환됩니다. 이번 업그레이드로 테이블 잠금이 발생하거나 테이블에 대한 기존 작업이 영향을 받지 않습니다. 이 릴리스로 업그레이드할 때 무중단이 예상됩니다. 포함된 SQL 쿼리는 다음과 같습니다.

CREATE TABLE IF NOT EXISTS ContentFlaggingCommonReviewers (
    UserId VARCHAR(26) PRIMARY KEY
);

CREATE TABLE IF NOT EXISTS ContentFlaggingTeamSettings (
    TeamId VARCHAR(26) PRIMARY KEY,
    Enabled BOOLEAN
);

CREATE TABLE IF NOT EXISTS ContentFlaggingTeamReviewers (
    TeamId VARCHAR(26),
    UserId VARCHAR(26),
    PRIMARY KEY (TeamId, UserId)
);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_contentflaggingteamreviewers_userid ON ContentFlaggingTeamReviewers (userid);
v11.0

GitLab SSO는 Team Edition에서 사용이 중단되었습니다. GitLab SSO를 사용하는 배포 환경은 새 무료 제품인 Mattermost Entry로 전환하는 동안 v10.11 ESR(보안 업데이트 12개월 제공)에 머물러 있을 수 있으며, 상용 또는 비영리 옵션을 검토할 수도 있습니다. 자세한 내용은 이 포럼 게시글을 참고하세요.

TeamSettings.ExperimentalViewArchivedChannels 설정은 사용이 중단되었습니다. 보관된 채널은 일반 채널 멤버십 규칙에 따라 항상 접근할 수 있습니다. 이 설정이 false로 지정되어 있으면 서버가 기동에 실패합니다. 보관된 채널에 대한 접근을 차단하려면 해당 채널을 비공개로 표시하고 관련 채널 멤버를 제거하세요. 자세한 내용은 이 포럼 게시글을 참고하세요.

Playbooks는 Team Edition에서 동작을 중단했습니다. Entry, Professional, Enterprise, Enterprise Advanced 플랜은 다운타임 없이 Playbooks v2로 자동 업그레이드됩니다. 자세한 내용은 이 포럼 게시글을 참고하세요.

실험적 Bleve Search 기능은 폐기되었습니다. Bleve가 활성화되어 있으면 DisableDatabaseSearch를 false로 설정하기 전까지 검색이 동작하지 않습니다. 자세한 내용은 이 포럼 게시글을 참고하세요.

MySQL 지원이 종료되었습니다. 마이그레이션 가이드에 PostgreSQL로 이전하기 위한 절차와 도구, 지원 내용이 정리되어 있습니다. 자세한 내용은 이 포럼 게시글을 참고하세요.

웹 앱 플러그인 API의 registerPostDropdownMenuComponent 훅이 제거되고 registerPostDropdownMenuAction으로 대체되었습니다. 자세한 내용은 이 포럼 게시글을 참고하세요.

웹 앱은 더 이상 웹 앱 플러그인이 사용할 수 있도록 Styled Components 의존성을 노출하지 않습니다. 자세한 내용은 이 포럼 게시글을 참고하세요.

Omnibus 지원은 사용이 중단되었습니다. 마지막 mattermost-omnibus 릴리스는 v10.12였습니다. 자세한 내용은 이 포럼 게시글을 참고하세요.

api/v4/ldap/sync에서 사용이 중단되었던 include_removed_members 옵션이 제거되었습니다. 관리자는 LDAP 설정 ReAddRemovedMembers를 사용할 수 있습니다.

NPS 플러그인을 활성화한 고객은 이 플러그인이 더 이상 텔레메트리를 통해 피드백을 전송하지 않으므로 제거해도 됩니다.

/api/v4/config/client 엔드포인트의 format 쿼리 파라미터 요구 사항은 사용이 중단되었습니다.

사용이 중단된 mmctl 명령과 플래그를 제거했습니다.

  • channel add - channel users add 사용
  • channel remove - channel users remove 사용
  • channel restore - channel unarchive 사용
  • channel make-private - channel modify --private 사용
  • command delete - command archive 사용
  • permissions show - permissions role show 사용
  • mmctl user email - mmctl user edit email 사용
  • mmctl user username - mmctl user edit username 사용

실험적 인증서 기반 인증 기능이 제거되었습니다. 서버를 기동하려면 ExperimentalSettings.ClientSideCertEnable이 false여야 합니다.

비밀번호 해싱 방식을 bcrypt에서 PBKDF2로 마이그레이션하는 로직을 추가했습니다. 마이그레이션은 점진적으로 진행되어, 사용자가 비밀번호를 입력하는 시점(예: 로그인하거나 민감한 작업을 위해 비밀번호를 재확인할 때) 해당 사용자의 비밀번호가 마이그레이션됩니다. 사용자가 계정에 로그인하지 못하게 되는 예외 상황이 하나 있습니다. 서버를 v11로 업그레이드한 뒤 사용자 A가 로그인(즉, 비밀번호를 입력)했는데 이후 서버를 v10.12 이하로 다운그레이드하면 사용자 A는 더 이상 로그인할 수 없습니다. 이 경우 관리자는 시스템 콘솔이나 mmctl user reset-password [users] 명령으로 해당 사용자의 비밀번호를 직접 재설정해야 합니다. 새 비밀번호 해싱 방식은 CPU를 더 많이 사용합니다. 비밀번호 기반 로그인을 사용하는 서버의 관리자는 다수의 사용자가 동시에 로그인하는 시간대의 성능을 모니터링해야 합니다.

/api/v4/teams/{team_id}/channels/search_archived는 사용이 중단되었으며 deleted 파라미터를 사용하는 /api/v4/channels/search로 대체되었습니다.

데이터베이스 연결 풀 기본 설정이 변경되었습니다. MaxOpenConns는 300에서 100으로, MaxIdleConns는 20에서 50으로 조정되어 데이터베이스 연결 관리에 더 적합한 2:1 비율을 갖게 되었습니다.

알림 로그 전용 파일은 사용이 중단되었습니다. 알림 로그를 계속 별도 로그 파일로 남기려면 AdvancedLoggingJSON 설정을 사용할 수 있습니다. 사용할 수 있는 설정 예시는 다음과 같습니다.

{
  "LogSettings": {
    "AdvancedLoggingJSON": {
      "notifications_file": {
        "type": "file",
        "format": "json",
        "levels": [
            {"id": 300, "name": "NotificationError"},
            {"id": 301, "name": "NotificationWarn"},
            {"id": 302, "name": "NotificationInfo"},
            {"id": 303, "name": "NotificationDebug"},
            {"id": 304, "name": "NotificationTrace"}
        ],
        "options": {
            "filename": "notifications.log",
            "max_size": 100,
            "max_age": 0,
            "max_backups": 0,
            "compress": true
        },
        "maxqueuesize": 1000
      }
    }
  }
}

https://forum.mattermost.com/t/deprecation-notice-manual-plugin-deployment/21192 에 안내된 대로 수동으로 설치한 플러그인은 더 이상 지원하지 않습니다.

PostgreSQL v13 지원이 제거되었습니다. 새로운 최소 PostgreSQL 버전은 v14 이상입니다. 자세한 내용은 PostgreSQL 버전 정책 문서를 참고하세요.

v10.12

v10.12 릴리스에는 주요 업그레이드 참고 사항이 없습니다.

v10.11

데이터베이스 마이그레이션 번호 문제를 일으키는 버그가 있으므로 >=10.11.17에서 <=11.7.2로 업그레이드하지 마세요. 이 버그는 v11.7.3에서 수정되었습니다.

v10.11.11부터 Photoshop Document(PSD) 파일은 더 이상 인라인으로 미리 보기되지 않으며 일반 첨부 파일로 처리됩니다.

Mattermost v10.11.4는 SSO 인증 흐름의 보안 강화 작업의 일환으로 새 API 엔드포인트 /api/v4/users/login/sso/code-exchange를 도입합니다. SSO(예: SAML)를 사용하는 고객은 네트워크 설정에서 이 엔드포인트로의 트래픽이 허용되는지 확인해야 합니다.

v10.11.17부터 role_updated WebSocket 이벤트가 전역 브로드캐스트 대신 영향을 받는 팀/채널로 범위가 좁혀져 성능이 개선되고 불필요한 네트워크 트래픽이 줄어듭니다. 마이그레이션은 업그레이드 중 자동으로 실행되므로 관리자가 수동으로 취해야 할 조치는 없습니다. 다운타임은 예상되지 않으며, 마이그레이션이 정상 운영 중에도 실행될 수 있으므로 업그레이드를 위한 별도 계획도 필요하지 않습니다. 모든 설치 환경이 이 변경의 영향을 받지만, roles 테이블은 규모가 큰 설치 환경에서도 1만 행에 훨씬 못 미치는 작은 테이블이므로 영향은 미미합니다. 이번 업그레이드에는 역할 스킴 조회를 효율화하기 위해 roles 테이블에 schemeid 컬럼을 추가하는 자동 데이터베이스 마이그레이션이 포함됩니다. 마이그레이션은 PostgreSQL의 비차단 기법을 사용합니다. 먼저 짧은 ACCESS EXCLUSIVE 잠금(카탈로그 전용 작업)으로 컬럼을 추가하고, 다음으로 ROW EXCLUSIVE 잠금으로 기존 데이터를 백필하며, 마지막으로 다른 작업을 차단하지 않는 SHARE UPDATE EXCLUSIVE 잠금으로 인덱스를 동시에 생성합니다. roles 테이블의 크기는 제한적이며 posts, reactions 등 대량 데이터의 영향을 받지 않습니다. 이 마이그레이션은 완전히 하위 호환됩니다.

v10.11.21 마이그레이션은 threadmemberships 테이블에서 고아 행, 구체적으로는 연결된 사용자가 해당 스레드가 속한 채널의 멤버가 더 이상 아닌 행을 제거합니다. 정리는 threadmemberships, threads, channelmembers 세 테이블을 조인한 필터링된 DELETE로 수행됩니다. 테이블, 컬럼, 인덱스 등 스키마 객체가 추가되거나 제거되지는 않으며 데이터 행만 영향을 받습니다. 대규모 인스턴스 참고 사항: 이 마이그레이션은 threads 및 channelmembers와의 조인을 포함해 threadmemberships 전체를 스캔하는 단일 비배치 삭제를 실행합니다. 행이 수천만 개에 이르거나 채널 멤버십 변동 이력이 많은 데이터베이스에서는 이 쿼리가 오래 실행되어 threadmemberships의 autovacuum을 차단하고 WAL 부하를 높이며 복제 지연을 유발할 수 있습니다. 대규모 배포 환경의 관리자는 업그레이드 전에 대표 데이터셋에서 실행 시간을 검증하고, 트래픽이 적은 시간대에 업그레이드를 계획하는 것이 좋습니다. 삭제된 행은 복구할 수 없으므로 다운 마이그레이션에는 롤백 SQL이 없습니다. 이 마이그레이션은 되돌릴 수 없습니다. 이 마이그레이션은 완전히 하위 호환되며 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 포함된 SQL 쿼리는 다음과 같습니다.

-- Drop ThreadMembership rows whose user is no longer a member of the thread's channel.
DELETE FROM threadmemberships WHERE (postid, userid) IN (
    SELECT
        threadmemberships.postid,
        threadmemberships.userid
    FROM
        threadmemberships
        JOIN threads ON threads.postid = threadmemberships.postid
        LEFT JOIN channelmembers ON channelmembers.userid = threadmemberships.userid
            AND threads.channelid = channelmembers.channelid
    WHERE
        channelmembers.channelid IS NULL
);
v10.10

Channels 테이블에 새 컬럼 DefaultCategoryName을 추가했습니다. 이 컬럼은 널을 허용하며, 새 사용자가 채널에 참여할 때 추가·생성할 카테고리 이름을 저장합니다. ExperimentalChannelCategorySetting이 활성화된 경우에만 사용됩니다. 이 마이그레이션은 완전히 하위 호환되며, 이번 업그레이드로 테이블 잠금이 발생하거나 테이블에 대한 기존 작업이 영향을 받지 않습니다. 이 릴리스로 업그레이드할 때 무중단이 예상됩니다. 포함된 SQL 쿼리는 다음과 같습니다.

PostgreSQL: https://github.com/mattermost/mattermost/blob/release-10.10/server/channels/db/migrations/postgres/000138_add_default_category_name_to_channel.down.sql 및 https://github.com/mattermost/mattermost/blob/release-10.10/server/channels/db/migrations/postgres/000138_add_default_category_name_to_channel.up.sql

MySQL: https://github.com/mattermost/mattermost/blob/release-10.10/server/channels/db/migrations/mysql/000138_add_default_category_name_to_channel.down.sql 및 https://github.com/mattermost/mattermost/blob/release-10.10/server/channels/db/migrations/mysql/000138_add_default_category_name_to_channel.up.sql

PostAcknowledgements 테이블에 새 컬럼 RemoteId와 ChannelId를 추가했습니다. 이 컬럼들은 널을 허용하며, 게시물에 확인 응답이 설정될 때 해당 게시물이 작성된 원격 ID(사용 가능한 경우)와 채널 ID를 저장합니다. 이 마이그레이션은 완전히 하위 호환되며, 이번 업그레이드로 테이블 잠금이 발생하거나 테이블에 대한 기존 작업이 영향을 받지 않습니다. 이 릴리스로 업그레이드할 때 무중단이 예상됩니다. 포함된 SQL 쿼리는 다음과 같습니다.

PostgreSQL: 000141_add_remoteid_channelid_to_post_acknowledgements.down.sql 및 000141_add_remoteid_channelid_to_post_acknowledgements.up.sql

MySQL: https://github.com/mattermost/mattermost/blob/release-10.10/server/channels/db/migrations/mysql/000141_add_remoteid_channelid_to_post_acknowledgements.down.sql 및 https://github.com/mattermost/mattermost/blob/release-10.10/server/channels/db/migrations/mysql/000141_add_remoteid_channelid_to_post_acknowledgements.up.sql

SharedChannelRemotes 테이블에 새 컬럼 LastMembersSyncAt을 추가하고 SharedChannelUsers에 LastMembershipSyncAt을 추가했습니다. 이 컬럼들은 널을 허용하며, LastMembersSyncAt은 로컬 클러스터와 각 원격 클러스터 간에 채널 멤버십 데이터가 마지막으로 동기화된 시각(사용 가능한 경우)을 저장합니다. LastMembershipSyncAt 타임스탬프는 특정 사용자의 채널 멤버십 상태가 특정 원격 클러스터와 마지막으로 동기화된 시각을 추적합니다. 이 마이그레이션은 완전히 하위 호환되며, 이번 업그레이드로 테이블 잠금이 발생하거나 테이블에 대한 기존 작업이 영향을 받지 않습니다. 이 릴리스로 업그레이드할 때 무중단이 예상됩니다. 포함된 SQL 쿼리는 다음과 같습니다.

PostgreSQL: https://github.com/mattermost/mattermost/blob/release-10.10/server/channels/db/migrations/postgres/000140_add_lastmemberssyncat_to_sharedchannelremotes.down.sql 및 https://github.com/mattermost/mattermost/blob/release-10.10/server/channels/db/migrations/postgres/000140_add_lastmemberssyncat_to_sharedchannelremotes.up.sql

MySQL: https://github.com/mattermost/mattermost/blob/release-10.10/server/channels/db/migrations/mysql/000140_add_lastmemberssyncat_to_sharedchannelremotes.down.sql 및 https://github.com/mattermost/mattermost/blob/release-10.10/server/channels/db/migrations/mysql/000140_add_lastmemberssyncat_to_sharedchannelremotes.up.sql

RemoteClusters 테이블에 새 컬럼 LastGlobalUserSyncAt을 추가했습니다. 이 컬럼은 널을 허용하며, LastGlobalUserSyncAt은 각 원격 클러스터에 대해 마지막으로 성공한 전역 사용자 동기화의 타임스탬프를 추적해 증분 동기화를 가능하게 합니다. 이 마이그레이션은 완전히 하위 호환되며, 이번 업그레이드로 테이블 잠금이 발생하거나 테이블에 대한 기존 작업이 영향을 받지 않습니다. 이 릴리스로 업그레이드할 때 무중단이 예상됩니다. 포함된 SQL 쿼리는 다음과 같습니다.

PostgreSQL: https://github.com/mattermost/mattermost/blob/release-10.10/server/channels/db/migrations/postgres/000139_remoteclusters_add_last_global_user_sync_at.down.sql 및 https://github.com/mattermost/mattermost/blob/release-10.10/server/channels/db/migrations/postgres/000139_remoteclusters_add_last_global_user_sync_at.up.sql

MySQL: https://github.com/mattermost/mattermost/blob/release-10.10/server/channels/db/migrations/mysql/000139_remoteclusters_add_last_global_user_sync_at.down.sql 및 https://github.com/mattermost/mattermost/blob/release-10.10/server/channels/db/migrations/mysql/000139_remoteclusters_add_last_global_user_sync_at.up.sql

v10.9

쿼리 성능을 개선하기 위해 SidebarChannels 테이블의 CategoryId 컬럼에 새 인덱스를 추가했습니다. 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. PostgreSQL에서는 8코어·16GB RAM 인스턴스 기준으로 120만 행 테이블에 인덱스를 추가하는 데 약 2초가 걸립니다. MySQL에서는 8코어·16GB RAM 인스턴스 기준으로 30만 행 테이블에 약 5초가 걸립니다. 이 마이그레이션은 완전히 하위 호환되며, 이번 업그레이드로 테이블 잠금이 발생하거나 테이블에 대한 기존 작업이 영향을 받지 않습니다. 이 릴리스로 업그레이드할 때 무중단이 예상됩니다. 포함된 SQL 쿼리는 다음과 같습니다.

PostgreSQL:

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_sidebarchannels_categoryid ON sidebarchannels(categoryid);

MySQL:

CREATE INDEX idx_sidebarchannels_categoryid ON SidebarChannels(CategoryId);

사용자 어트리뷰트를 별도 테이블로 집계하는 새 구체화 뷰(AttributeView) 형태의 스키마 변경이 추가되었습니다. 이번 업그레이드에서 데이터베이스 다운타임은 예상되지 않습니다. 이전 버전의 Mattermost도 새 스키마 변경과 함께 동작할 수 있습니다. 스키마 변경에 포함된 SQL 쿼리는 다음과 같습니다.

PostgreSQL: https://github.com/mattermost/mattermost/blob/release-10.9/server/channels/db/migrations/postgres/000137_update_attribute_view.up.sql

MySQL: https://github.com/mattermost/mattermost/blob/release-10.9/server/channels/db/migrations/mysql/000136_create_attribute_view.up.sql

SQL 쿼리를 제시하는 목적은 다운타임을 줄이는 것이 아니라, 관리자가 Mattermost를 업그레이드하지 않고도 스키마 변경을 미리 적용할 수 있도록 하는 것입니다. 그렇게 하면 실제로 Mattermost를 업그레이드할 때 변경이 이미 적용되어 있으므로 업그레이드가 즉시 끝납니다.

SamlSettings.EnableSyncWithLdap이 활성화되어 있으면 이제 Mattermost는 로그인 시 해당 사용자가 연결된 LDAP 서버에 존재하는지 확인합니다. LDAP 서버에 사용자가 없으면 로그인이 실패합니다. 이전에는 LDAP 서버에 없는 사용자도 로그인할 수 있었고 다음 LDAP 동기화 때 비활성화되었습니다.

v10.8

새 테이블 AccessControlPolicies와 AccessControlPolicyHistory가 생성됩니다. 이 마이그레이션은 완전히 하위 호환되고 잠금을 걸지 않으며 무중단이 예상됩니다.

레거시 SKU인 E10 및 E20 라이선스 지원이 제거되었습니다. 도움이 필요하면 Mattermost 전문가에게 문의하세요.

v10.7

곧 출시될 라이선스 기능의 메타데이터를 저장하기 위해 Channels 테이블에 새 컬럼 BannerInfo를 추가했습니다. 이 마이그레이션은 완전히 하위 호환되고 잠금을 걸지 않으며 무중단이 가능합니다. 스키마 변경에 포함된 SQL 쿼리는 다음과 같습니다.

PostgreSQL:

ALTER TABLE channels ADD COLUMN IF NOT EXISTS bannerinfo jsonb;

MySQL:

SET @preparedStatement = (SELECT IF(

   NOT EXISTS(

       SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS

       WHERE table_name = 'Channels'

       AND table_schema = DATABASE()

       AND column_name = 'BannerInfo'

   ),

   'ALTER TABLE Channels ADD COLUMN BannerInfo json;',

   'SELECT 1;'

));

PREPARE addColumnIfNotExists FROM @preparedStatement;
EXECUTE addColumnIfNotExists;
DEALLOCATE PREPARE addColumnIfNotExists;

속성 아키텍처 테이블에 커서 기반 페이지네이션 지원을 추가했으며, 여기에는 인덱스를 생성하는 SQL 마이그레이션이 포함됩니다. 이 마이그레이션은 완전히 하위 호환되고 잠금을 걸지 않으며 무중단이 가능합니다. 스키마 변경에 포함된 SQL 쿼리는 다음과 같습니다.

MySQL:

SET @preparedStatement = (SELECT IF(
    (
        SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS
        WHERE table_name = 'PropertyValues'
        AND table_schema = DATABASE()
        AND index_name = 'idx_propertyvalues_create_at_id'
    ) > 0,
    'SELECT 1',
    'CREATE INDEX idx_propertyvalues_create_at_id ON PropertyValues(CreateAt, ID);'
));

PREPARE createIndexIfNotExists FROM @preparedStatement;
EXECUTE createIndexIfNotExists;
DEALLOCATE PREPARE createIndexIfNotExists;
SET @preparedStatement = (SELECT IF(
    (
        SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS
        WHERE table_name = 'PropertyFields'
        AND table_schema = DATABASE()
        AND index_name = 'idx_propertyfields_create_at_id'
    ) > 0,
    'SELECT 1',
    'CREATE INDEX idx_propertyfields_create_at_id ON PropertyFields(CreateAt, ID);'
));

PREPARE createIndexIfNotExists FROM @preparedStatement;
EXECUTE createIndexIfNotExists;
DEALLOCATE PREPARE createIndexIfNotExists;

PostgreSQL:

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_propertyvalues_create_at_id ON PropertyValues(CreateAt, ID)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_propertyfields_create_at_id ON PropertyFields(CreateAt, ID)
v10.6

PostgreSQL v11과 v12 지원이 제거되었습니다. 새로운 최소 PostgreSQL 버전은 v13 이상입니다. 자세한 내용은 최소 지원 PostgreSQL 버전 정책 문서를 참고하세요.

시스템 콘솔 통계가 PostgreSQL에서 더 빠르게 동작합니다. MaxUsersForStatistics 설정은 이제 User counts with posts 차트만 비활성화하며 나머지 통계에는 영향을 주지 않습니다. 나머지 쿼리는 이제 항상 빠르게 처리되므로 이를 비활성화하기 위해 해당 설정값이 더 이상 필요하지 않기 때문입니다. 게시물 수와 파일 수는 하루 단위로 갱신되므로 항상 실시간 데이터를 반영하지는 않습니다. 선 그래프나 플러그인 데이터 같은 고급 통계는 클릭하기 전까지 숨겨져 로딩 시간이 줄어듭니다. 이 최적화는 v11에서 완전한 지원 중단이 예정된 MySQL에는 적용되지 않습니다. 더 나은 성능과 장기적인 지원을 위해 PostgreSQL로 이전하는 것을 권장합니다. 마이그레이션 시간: 게시물 1,200만 건, fileinfo 항목 100만 건인 시스템에서 마이그레이션에 15초가 걸리지만, 서버의 테이블 크기와 데이터베이스 사양에 따라 수 분이 걸릴 수도 있습니다. 이 마이그레이션은 잠금을 걸지 않습니다. 이 최적화는 PostgreSQL에만 적용되므로 MySQL 배포 환경을 위한 마이그레이션은 없습니다. 스키마 변경에 포함된 SQL 쿼리는 다음과 같습니다.

CREATE MATERIALIZED VIEW IF NOT EXISTS posts_by_team_day as
SELECT to_timestamp(p.createat/1000)::date as day, COUNT(*) as num, teamid
FROM posts p JOIN channels c on p.channelid=c.id
GROUP BY day, c.teamid;

CREATE MATERIALIZED VIEW IF NOT EXISTS bot_posts_by_team_day as
SELECT to_timestamp(p.createat/1000)::date as day, COUNT(*) as num, teamid
FROM posts p
JOIN Bots b ON p.UserId = b.Userid
JOIN channels c on p.channelid=c.id
GROUP BY day, c.teamid;

CREATE MATERIALIZED VIEW IF NOT EXISTS file_stats as
SELECT COUNT(*) as num, COALESCE(SUM(Size), 0) as usage
FROM fileinfo
WHERE DeleteAt = 0;
v10.5

Mattermost v10.5.0과 v10.5.1에는 플러그인 설정이 사라지는 버그가 있는 Jira 플러그인(v4.2.0)이 번들로 포함되어 있습니다. 장애 가능성을 피하기 위해 이 버전으로의 업그레이드를 강력히 권장하지 않습니다. 이미 v10.5.0 또는 v10.5.1로 업그레이드했다면 Jira 플러그인을 v4.2.1로 갱신하거나, 가능하면 Mattermost와 Jira 플러그인을 모두 최신 버전으로 업그레이드하는 것을 권장합니다.

웹 앱의 PluginLinkComponent 내부 동작이 변경되어 기본적으로 링크 툴팁을 DOM에서 언마운트하게 되었고, 그 결과 성능이 크게 향상되었습니다. registerLinkTooltipComponent로 링크 툴팁을 등록하는 플러그인은 툴팁 컴포넌트 관리 방식이 달라진 것을 경험하게 됩니다. 이제 툴팁 컴포넌트는 링크에 마우스를 올리거나 포커스가 갈 때만 마운트됩니다. 따라서 플러그인은 마운트·언마운트 상황을 올바르게 처리하도록 컴포넌트를 수정해야 할 수 있습니다. 예를 들어 mattermost-plugin-jira에서는 componentDidUpdate 라이프사이클 훅을 componentDidMount로 교체했습니다. 플러그인의 툴팁 컴포넌트가 함수형 React 컴포넌트라면 빈 의존성 배열을 가진 useEffect가 이를 처리하므로 대부분 자동으로 동작할 가능성이 높습니다.

Mattermost 서버는 플러그인 수동 배포 지원을 중단했습니다. 수동 배포란 관리자나 배포 자동화가 플러그인 번들의 내용을 서버 작업 디렉터리에 복사하는 방식을 말합니다. 수동 또는 자동화된 배포 워크플로가 여전히 필요하다면 관리자는 대신 플러그인 번들을 사전 패키징할 수 있습니다. 자세한 내용은 이 포럼 게시글을 참고하세요.

Mattermost는 Microsoft Windows 운영 체제용 공식 Mattermost 서버 빌드 제공을 중단했습니다. 관리자는 기존 Mattermost 서버 설치 환경을 공식 Linux 빌드로 이전해야 합니다. 자세한 내용은 이 포럼 게시글을 참고하세요.

v10.5는 컴플라이언스 내보내기 기능을 개편하여 내보낸 데이터의 구조·저장·처리 방식을 변경합니다. 이 변경은 주로 시스템 관리자에게 영향을 주며, 주요 내용은 아래와 같습니다. 자세한 내용은 컴플라이언스 내보내기 문서를 참고하세요.

출력 파일과 디렉터리가 변경되었습니다 - 이전에는 모든 배치 디렉터리를 담은 zip 파일 하나를 내보냈습니다. 이제는 디렉터리 하나를 내보내고, 그 아래에서 각 배치가 각각의 zip 파일이 됩니다.

컴플라이언스 내보내기 성능이 개선되었습니다 - 컴플라이언스 내보내기가 이제 최소 50% 이상 빨라졌습니다.

로직이 개선되었습니다 - 발견된 문제를 개선하고 버그를 수정했습니다.

내보내기 유형별 변경 사항 - 내보내기 출력 형식이 변경되었습니다. 일부 필드의 의미가 명확해졌고 새 필드도 여럿 추가되었습니다. 로직 버그를 수정하면서도 하위 호환성을 유지하는 것이 목표였습니다.

자세한 내용은 컴플라이언스 내보내기 제품 문서를 참고하세요.

Property System Architecture 기능의 일환으로 Mattermost v10.5는 스키마에 새 테이블을 추가하는 마이그레이션을 실행합니다. 이 마이그레이션은 새 테이블과 인덱스만 생성하므로 기존 데이터에는 영향이 없습니다.

새 테이블 PropertyGroups, PropertyFields, PropertyValues가 생성됩니다.

PostgreSQL에서는 PropertyFields 테이블의 Type 컬럼에 사용할 새 열거형 타입 property_field_type이 생성됩니다.

PropertyFields와 PropertyValues 테이블에는 고유 제약이 있어 MySQL에서는 인덱스가 생성되며, PostgreSQL에서는 이 제약을 강제하기 위해 UNIQUE INDEX를 직접 사용합니다.

PropertyValues 테이블에 새 인덱스 idx_propertyvalues_targetid_groupid가 생성됩니다.

이 마이그레이션은 데이터 없이 새 테이블만 생성합니다. 마이그레이션은 하위 호환되며 이전 버전의 Mattermost도 새 스키마 변경과 함께 동작할 수 있습니다. 이번 업그레이드로 테이블 잠금이 발생하거나 테이블에 대한 기존 작업이 영향을 받지 않습니다. 이 릴리스로 업그레이드할 때 무중단이 가능합니다.

스키마 변경에 포함된 SQL 쿼리는 다음과 같습니다.

MySQL:

CREATE TABLE IF NOT EXISTS PropertyGroups (
 ID varchar(26) PRIMARY KEY,
 Name varchar(64) NOT NULL,
 UNIQUE(Name)
);

CREATE TABLE IF NOT EXISTS PropertyFields (
 ID varchar(26) PRIMARY KEY,
 GroupID varchar(26) NOT NULL,
 Name varchar(255) NOT NULL,
 Type enum('text', 'select', 'multiselect', 'date', 'user', 'multiuser'),
 Attrs json,
 TargetID varchar(255),
 TargetType varchar(255),
 CreateAt bigint(20),
 UpdateAt bigint(20),
 DeleteAt bigint(20),
 UNIQUE(GroupID, TargetID, Name, DeleteAt)
);

CREATE TABLE IF NOT EXISTS PropertyValues (
 ID varchar(26) PRIMARY KEY,
 TargetID varchar(255) NOT NULL,
 TargetType varchar(255) NOT NULL,
 GroupID varchar(26) NOT NULL,
 FieldID varchar(26) NOT NULL,
 Value json,
 CreateAt bigint(20),
 UpdateAt bigint(20),
 DeleteAt bigint(20),
 UNIQUE(GroupID, TargetID, FieldID, DeleteAt)
);

SET @preparedStatement = (SELECT IF(
 (
    SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS
    WHERE table_name = 'PropertyValues'
    AND table_schema = DATABASE()
    AND index_name = 'idx_propertyvalues_targetid_groupid'
 ) > 0,
 'SELECT 1',
 'CREATE INDEX idx_propertyvalues_targetid_groupid ON PropertyValues (TargetID, GroupID);'
));

PREPARE createIndexIfNotExists FROM @preparedStatement;
EXECUTE createIndexIfNotExists;
DEALLOCATE PREPARE createIndexIfNotExists;

PostgreSQL:

CREATE TABLE IF NOT EXISTS PropertyGroups (
 ID varchar(26) PRIMARY KEY,
 Name varchar(64) NOT NULL,
 UNIQUE(Name)
);

DO
BEGIN
  IF NOT EXISTS (SELECT * FROM pg_type typ
                        INNER JOIN pg_namespace nsp ON nsp.oid = typ.typnamespace
                    WHERE nsp.nspname = current_schema()
                        AND typ.typname = 'property_field_type') THEN
 CREATE TYPE property_field_type AS ENUM (
    'text',
    'select',
    'multiselect',
    'date',
    'user',
    'multiuser'
 );
  END IF;
END;
LANGUAGE plpgsql;

CREATE TABLE IF NOT EXISTS PropertyFields (
 ID varchar(26) PRIMARY KEY,
 GroupID varchar(26) NOT NULL,
 Name varchar(255) NOT NULL,
 Type property_field_type,
 Attrs jsonb,
 TargetID varchar(255),
 TargetType varchar(255),
 CreateAt bigint NOT NULL,
 UpdateAt bigint NOT NULL,
 DeleteAt bigint NOT NULL
);

CREATE UNIQUE INDEX IF NOT EXISTS idx_propertyfields_unique ON PropertyFields (GroupID, TargetID, Name) WHERE DeleteAt = 0;

CREATE TABLE IF NOT EXISTS PropertyValues (
 ID varchar(26) PRIMARY KEY,
 TargetID varchar(255) NOT NULL,
 TargetType varchar(255) NOT NULL,
 GroupID varchar(26) NOT NULL,
 FieldID varchar(26) NOT NULL,
 Value jsonb NOT NULL,
 CreateAt bigint NOT NULL,
 UpdateAt bigint NOT NULL,
 DeleteAt bigint NOT NULL
);

CREATE UNIQUE INDEX IF NOT EXISTS idx_propertyvalues_unique ON PropertyValues (GroupID, TargetID, FieldID) WHERE DeleteAt = 0;
CREATE INDEX IF NOT EXISTS idx_propertyvalues_targetid_groupid ON PropertyValues (TargetID, GroupID);
    ``
v10.4

v10.4 릴리스에는 주요 업그레이드 참고 사항이 없습니다.

v10.3

클래식 모바일 앱은 단계적으로 종료되었습니다. Apple App Store 또는 Google Play Store에서 새 v2 모바일 앱을 내려받으세요. 자세한 내용은 클래식 모바일 앱 지원 중단 Mattermost 포럼 게시글을 참고하세요.

v10.2

Docker 이미지 아티팩트 서명에 사용하던 Docker Content Trust(DCT)가 v10.2(2024년 11월)에서 Sigstore Cosign으로 대체되었습니다. DCT를 사용한 아티팩트 검증에 의존하고 있다면 Cosign 사용으로 전환하세요. 자세한 내용은 DCT 지원 중단 예정 Mattermost 포럼 게시글을 참고하세요.

v10.1

v10.1 릴리스에는 주요 업그레이드 참고 사항이 없습니다.

v10.0

v10부터는 MySQL을 사용하는 신규 설치를 더 이상 지원하지 않습니다. 모든 신규 고객 및 배포 환경은 최소 지원 버전 이상의 PostgreSQL 데이터베이스에서만 지원됩니다. MySQL 지원 종료는 Mattermost v11을 목표로 하고 있습니다.

Apps Framework는 신규 설치에 대해 사용이 중단되었습니다. 웹훅, 슬래시 명령, OAuth2 앱, 플러그인을 사용해 Mattermost를 확장하세요.

Mattermost v10은 모든 Enterprise 라이선스 고객에게 Playbooks v2를 제공합니다. Professional SKU 고객은 2025년 9월까지 유지·지원되는 Playbooks v1을 중단 없이 계속 사용할 수 있으며, 이후에는 적절한 기존 사용자 유예 전략이 적용됩니다. 더 자세한 정보와 논의는 포럼에서 확인할 수 있습니다.

System Console의 채널 관리 섹션에서 Channel Moderation의 이름이 Advanced Access Control로 변경되었습니다.

공지 배너 기능의 이름이 “system-wide notifications”로 변경되었습니다.

시스템 콘솔에서 “Collapsed Reply Threads”의 이름이 “Threaded Discussions”로 변경되었습니다.

시스템 콘솔에서 “System Roles”의 이름이 “Delegated Granular Administration”으로 변경되었습니다.

SSO 로그인에서 "Office 365"의 이름이 "Entra ID"로 변경되었습니다.

이미지 프록시가 비활성화된 경우 /api/v4/image 엔드포인트는 완전히 사용이 중단되었습니다.

Calls 플러그인 v1.0.1이 사전 패키징되었습니다. 여기에는 라이선스가 적용된 서버의 지원성과 품질에 집중하기 위해 라이선스가 없는 서버에서 그룹 통화를 제거하는 등 호환성 파괴 변경이 포함됩니다. 라이선스가 없는 서버도 활동 대부분을 차지하는 다이렉트 메시지 채널에서는 Calls를 계속 사용할 수 있습니다.

사용이 중단되었던 Config.ProductSettings, LdapSettings.Trace, AdvancedLoggingConfig 설정 필드를 제거했습니다.

대부분의 API 엔드포인트에서 사용이 중단되었던 pageSize 쿼리 파라미터를 제거했습니다.

실험적 기능인 엄격한 CSRF 토큰 강제는 사용이 중단되었습니다. 이 기능은 Mattermost v11에서 완전히 제거됩니다.

v9.11

Elasticsearch v8 지원을 추가했습니다. 또한 OpenSearch v1.x와 v2.x에 대한 베타 지원을 추가했습니다. Elasticsearch와 OpenSearch를 구분하기 위해 새 설정 ElasticsearchSettings.Backend가 추가되었습니다. 기본값은 elasticsearch이며, 공식 v8 클라이언트는 Elasticsearch v7.11 이상에서만 동작하므로 AWS Elasticsearch v7.10.x 지원이 중단됩니다.

참고
  • OpenSearch를 사용하는 AWS 고객은 Mattermost 설정을 elasticsearch에서 opensearch로 변경하고 호환 모드를 비활성화해야 합니다. 업그레이드에 관한 자세한 내용은 OpenSearch 문서를 참고하세요.
  • Mattermost 서버를 업그레이드한 뒤 mmctl을 사용하거나 설정을 직접 편집한 다음 Mattermost 서버를 재시작하세요.
  • OpenSearch를 사용한다면 백엔드를 반드시 opensearch로 설정해야 합니다. 그렇지 않으면 Mattermost가 동작하지 않습니다.

Elasticsearch v8을 사용한다면 와일드카드 연산이 동작하도록 elasticsearch.yml에서 action.destructive_requires_name을 false로 설정하세요.

v9.10

v9.10 릴리스에는 주요 업그레이드 참고 사항이 없습니다.

v9.9

v9.9 릴리스에는 주요 업그레이드 참고 사항이 없습니다.

v9.8

v9.8 릴리스에는 주요 업그레이드 참고 사항이 없습니다.

v9.7

v9.7 릴리스에는 주요 업그레이드 참고 사항이 없습니다.

v9.6

v9.6 릴리스에는 주요 업그레이드 참고 사항이 없습니다.

v9.5

MySQL v5.7은 수명이 종료되어 지원을 중단했습니다. 가능한 한 빨리 MySQL 인스턴스를 업그레이드하시기 바랍니다.

엔터프라이즈 규모 및 액세스 제어 자동화 기능을 사용할 수 없는 상태에서 등록되어 있고 비활성화되지 않은 사용자 수가 10,000명을 초과하면, 컴파일된 Team Edition 및 Enterprise Edition 배포 환경에서 안전 한도 오류 메시지가 표시되도록 추가했습니다. ERROR_SAFETY_LIMITS_EXCEEDED.

v9.4

v9.4 릴리스에는 주요 업그레이드 참고 사항이 없습니다.

v9.3

v9.3 릴리스에는 주요 업그레이드 참고 사항이 없습니다.

v9.2

전역 보존 정책이 "keep-forever"로 설정되어 있어도 사용자 지정 보존 정책이 하나라도 활성화되어 있으면 작업이 실행되도록 데이터 보존 정책을 수정했습니다. 이 수정 이전에는 전역 데이터 보존 정책이 "keep-forever"로 설정되어 있거나 비활성화되어 있는 한 활성화된 사용자 지정 데이터 보존 정책이 실행되지 않았습니다. 수정 이후에는 전역 데이터 보존 정책이 "keep-forever"로 설정되어 있어도 사용자 지정 데이터 보존 정책이 자동으로 실행됩니다. 서버를 업그레이드하고 나면 의도치 않게 게시물이 삭제될 수 있습니다. 관리자는 업그레이드 전에 모든 사용자 지정 데이터 보존 정책을 비활성화했다가 업그레이드 후 다시 활성화해야 합니다.

v9.1

지원되는 최소 데스크톱 앱 버전이 v5.3이 되었습니다. OAuth/SAML 흐름에 desktop_login이 포함되도록 변경되어 이전 버전과는 호환되지 않습니다.

v9.0

사용이 중단되었던 Insights 기능을 제거했습니다.

Mattermost Boards를 비롯한 여러 플러그인이 전적으로 커뮤니티 지원 방식으로 전환되었습니다. 자세한 내용은 포럼 게시글을 참고하세요.

channel_viewed 웹소켓 이벤트가 multiple_channels_viewed로 변경되었으며, 이제 실제로 읽지 않은 메시지가 있는 채널에 대해서만 발생합니다.

v8.1

v8.1.2에서 데이터 보존 DeleteOrphanedRows 쿼리의 성능을 개선했습니다.

새 테이블을 위한 새 마이그레이션:

MySQL:

CREATE TABLE

IF NOT EXISTS
  RetentionIdsForDeletion(Id
    VARCHAR(26) NOT NULL,
    TableName VARCHAR(64),
    Ids json, PRIMARY KEY (Id
      ), KEY
    idx_retentionidsfordeletion_tablename
    (TableName)) ENGINE =
  InnoDB DEFAULT CHARSET =
  utf8mb4;
  ``

PostgreSQL:

CREATE TABLE

IF NOT EXISTS
  retentionidsfordeletion(id
    VARCHAR(26) PRIMARY KEY,
    tablename VARCHAR(64),
    ids VARCHAR(26) []);
  CREATE INDEX

IF NOT EXISTS
  idx_retentionidsfordeletion_tablename
  ON retentionidsfordeletion(
    tablename);
  ``

사용자나 채널을 완전 삭제하면 이제 연결된 리액션도 함께 정리됩니다.

기능 플래그 DataRetentionConcurrencyEnabled를 제거했습니다. 데이터 보존은 성능 저하를 피하기 위해 이제 동시성 없이 실행됩니다.

고아 리액션을 삭제할 때 한 번에 가져올 ID 배치 수를 관리자가 설정할 수 있는 새 설정 DataRetentionSettings.RetentionIdsBatchSize를 추가했습니다. 기본값은 100입니다.

v8.0

Insights는 모든 신규 인스턴스와 v8.0으로 업그레이드하는 기존 서버에서 사용이 중단되었습니다. Insights가 왜 사용 중단되었는지에 대한 자세한 내용은 이 포럼 게시글을 참고하세요.

Focalboard 플러그인은 이제 모든 신규 인스턴스에서 기본적으로 비활성화되며 System Console > Plugin settings에서 활성화할 수 있습니다.

Channel Export 플러그인과 Apps 플러그인이 이제 기본적으로 비활성화됩니다.

Apps Bar가 이제 온프레미스 서버에서 기본적으로 활성화됩니다. ExperimentalSettings.EnableAppBar의 이름도 ExperimentalSettings.DisableAppBar로 변경되었습니다. 자세한 내용은 설정 항목 문서와 이 포럼 글을 참고하세요.

메인 server package에서 Go 모듈 경로가 github.com/mattermost/mattermost-server/server/v8에서 github.com/mattermost/mattermost/server/v8로 변경되었습니다. 다만 public 서브모듈이 도입되었으므로 서드파티 코드가 이 server 패키지를 import할 필요는 더 이상 없습니다.

익숙한 model과 plugin 패키지를 담고 있으면서 서버와는 별도로 버전이 매겨지는 public 서브모듈을 도입했습니다. 이제 Go 프로그램과 플러그인이 github.com/mattermost/mattermost-server/server/public을 import하고 Go 방식대로 버전을 관리할 수 있으므로, 특정 커밋 해시를 go get할 필요가 없습니다. 이 서브모듈은 아직 v1을 출시하지 않았고 API가 안정화되기 전까지 호환성 파괴 변경이 있을 예정이지만, Mattermost 서버 자체와는 상위·하위 호환성을 모두 유지합니다.

위 public 서브모듈 도입의 일환으로 이제 model.Client4 메서드에 context.Context가 전달됩니다.

PostgreSQL v10 지원이 제거되었습니다. 새로운 최소 PostgreSQL 버전은 v11입니다.

Go용 Mattermost 공개 API가 이제 별도로 버전이 매겨진 패키지로 제공됩니다. 특정 커밋 해시를 고정하는 대신 Go 방식대로 이 패키지를 의존성으로 추가하세요. go get github.com/mattermost/mattermost-server/server/public. 위치가 옮겨진 이 Go API는 Mattermost v7과 하위 호환됩니다. 또한 기존에 github.com/mattermost/mattermost-server/v6/model에 있던 Go API도 Mattermost v8과 상위 호환되지만 최신 기능은 포함되지 않을 수 있습니다. 플러그인을 다시 컴파일할 필요는 없으나, 개발자는 빌드 과정을 단순화하기 위해 새 패키지를 사용하도록 전환할 수 있습니다. 새 public 패키지는 올해 후반에 v1으로 출시하기 전 추가 코드 리팩터링 여지를 두기 위해 Mattermost v8과 함께 0.5.0 버전으로 제공됩니다.

여러 줄 JSON, 문자열로 이스케이프된 JSON, 또는 JSON이 담긴 파일을 가리키는 파일 이름을 지원하는 설정 필드 세 개(LogSettings.AdvancedLoggingJSON, ExperimentalAuditSettings.AdvancedLoggingJSON, NotificationLogSettings.AdvancedLoggingJSON)가 추가되었습니다. 기존 AdvancedLoggingConfig 필드는 사용이 중단되었습니다.

Go MySQL 드라이버의 maxAllowedPacket 크기가 4MiB에서 64MiB로 변경되었습니다. 이는 MySQL 5.7에서 MySQL 8.0으로 넘어오며 서버 측 기본값이 바뀐 것과 맞추기 위한 것입니다. max_allowed_packet 설정이 64MiB가 아니라면 서버 측 값과 일치하도록 MySQL 설정 DSN에 maxAllowedPacket 파라미터를 추가해 갱신하세요. 또는 값을 0으로 지정하면 새 연결마다 서버 측 값을 자동으로 조회하지만 성능 부담이 따릅니다.

ExperimentalSettings.PatchPluginsReactDOM을 제거했습니다. 이 설정을 이전에 활성화했다면 다음을 확인하세요.

  1. Mattermost가 지원하는 모든 플러그인이 최신 버전으로 갱신되었는지 확인합니다.
  2. 그 밖의 플러그인이 React 17을 지원하도록 갱신되었는지 확인합니다. 자세한 내용은 v7.7 릴리스 항목을 참고하세요.

사용이 중단되었던 PermissionUseSlashCommands를 제거했습니다.

사용이 중단되었던 model.CommandArgs.Session을 제거했습니다.

사이트 URL의 출처가 아닌 다른 출처에서 웹소켓 연결을 허용하려는 서버는 ServiceSettings.AllowCorsFrom 설정을 지정하세요. 아울러 siteURL이 올바르게 설정되어 있는지도 확인하세요.

v8.0 릴리스에서 mattermost-server, mattermost-webapp, mmctl 저장소가 하나로 병합되었습니다. 개발자는 자세한 내용을 위해 갱신된 개발자 가이드를 읽어 보세요.

이전 릴리스의 마이그레이션으로 발생한 문제를 수정했습니다. 쿼리는 PostgreSQL 14 DB의 t3.medium RDS 인스턴스에서 약 11ms가 걸립니다. preferences 테이블에 대한 잠금은 삭제할 행이 있을 때만 획득되며 소요 시간은 무시할 수 있는 수준입니다.

사용자가 이미 나간 채널의 스레드를 스레드 화면에서 계속 보게 되는 문제를 수정했습니다. PostgreSQL 마이그레이션 실행 시간: Execution time: 58.11 sec, DELETE 2766690. MySQL 마이그레이션 실행 시간: Query OK, 2766769 rows affected (4 min 47.57 sec).

다른 출처에서 웹소켓 연결을 허용하려는 서버는 ServiceSettings.AllowCorsFrom 설정을 지정하세요.

channelID 컬럼을 테이블 자체에 비정규화하여 파일 정보 통계 쿼리를 최적화했습니다. 이로써 우측 패널을 선택할 때 채널의 파일 개수를 가져오는 쿼리가 빨라집니다. 마이그레이션 시간:

FileInfo 1,731행과 게시물 1,100만 건이 있는 PostgreSQL 12.14 DB에서 약 0.27초가 걸렸습니다.

FileInfo 1,405행과 게시물 1,100만 건이 있는 MySQL 8.0.31 DB에서 약 0.3초가 걸렸습니다.

v7.10

v7.10.1에서 이전 릴리스의 마이그레이션으로 발생한 문제를 수정했습니다. 쿼리는 PostgreSQL 14 DB의 t3.medium RDS 인스턴스에서 약 11ms가 걸립니다. preferences 테이블에 대한 잠금은 삭제할 행이 있을 때만 획득되며 소요 시간은 무시할 수 있는 수준입니다.

v7.10.1에서 사용자가 이미 나간 채널의 스레드를 스레드 화면에서 계속 보게 되는 문제를 수정했습니다. MySQL 마이그레이션 실행 시간: Query OK, 2766769 rows affected (4 min 47.57 sec). PostgreSQL 마이그레이션 실행 시간: 58.11 sec, DELETE 2766690.

v7.10.3에서, 사이트 URL의 출처가 아닌 다른 출처에서 웹소켓 연결을 허용하려는 서버는 ServiceSettings.AllowCorsFrom 설정을 지정하세요. 아울러 siteURL이 올바르게 설정되어 있는지도 확인하세요.

v7.9

Posts(OriginalId)에 새 인덱스를 추가했습니다. 게시물 1,180만 건이 있는 데이터베이스에서 i7-11800H CPU(8코어 16스레드), 32GiB RAM, SSD를 갖춘 장비 기준으로 인덱스 생성에 MySQL에서는 98.51초, PostgreSQL에서는 2.6초가 걸립니다.

  • PostgreSQL 데이터베이스에서는 인덱스를 생성하는 동안 Posts 테이블이 잠깁니다. 이 테이블이 잠기는 것을 피하려면 관리자가 업그레이드 전에 다음 비차단 쿼리로 인덱스를 직접 생성할 수 있습니다. CREATE INDEX CONCURRENTLY idx_posts_original_id ON Posts(OriginalId);.
  • 게시물 수가 적은 PostgreSQL 배포 환경을 운영하는 관리자라면 마이그레이션 과정에서 인덱스를 생성하도록 두고, 마이그레이션이 끝날 때까지 Posts 테이블이 잠긴 상태로 유지되는 것을 감수하는 편을 택할 수도 있습니다.

v7.9.4에서 사용자가 이미 나간 채널의 스레드를 스레드 화면에서 계속 보게 되는 문제를 수정했습니다. MySQL 마이그레이션 실행 시간: Query OK, 2766769 rows affected (4 min 47.57 sec). PostgreSQL 마이그레이션 실행 시간: 58.11 sec, DELETE 2766690.

v7.9.4에서 OAuth 2 관련 수정을 백포트했습니다. 쿼리 시간은 삭제할 행이 있는지 여부에 따라 달라집니다.

삭제할 행이 없는 경우:

  • MySQL v5.7.12: 9ms
  • PostgreSQL v10: 21ms

4행인 경우:

  • MySQL v5.7.12: 17.2ms
  • PostgreSQL v10: 9.9ms

위 시간은 다음 테이블 크기를 기준으로 측정한 값입니다.

  • Preferences: 200만 레코드
  • oauthaccessdata와 sessions: 10레코드

다음을 실행하면 영향을 받는 행의 수를 확인할 수 있습니다.

PostgreSQL:

SELECT count(o.*)
FROM oauthaccessdata o
 WHERE NOT EXISTS (
    SELECT p.*
    FROM preferences p
    WHERE o.clientid = p.name
      AND o.userid = p.
      userid
      AND p.category =
      'oauth_app'
    );

MySQL:

SELECT COUNT(o.`Token`)
FROM OAuthAccessData o
LEFT JOIN Preferences p ON o.
  clientid = p.name
  AND o.userid = p.userid
  AND p.category = 'oauth_app'
INNER JOIN Sessions s ON o.token = s
  .token
WHERE p.name IS NULL;

oauthaccessdata와 sessions 테이블에 대한 잠금은 삭제할 행이 있을 때만 획득됩니다.

v7.9.5에서, 사이트 URL의 출처가 아닌 다른 출처에서 웹소켓 연결을 허용하려는 서버는 ServiceSettings.AllowCorsFrom 설정을 지정하세요. 아울러 siteURL이 올바르게 설정되어 있는지도 확인하세요.

v7.8

Message Priority & Acknowledgement가 이제 모든 인스턴스에서 기본적으로 활성화됩니다. 이 기능은 시스템 콘솔의 Posts > Message Priority에서 또는 설정의 PostPriority 항목으로 비활성화할 수 있습니다.

v7.8.5에서 사용자가 이미 나간 채널의 스레드를 스레드 화면에서 계속 보게 되는 문제를 수정했습니다. MySQL 마이그레이션 실행 시간: Query OK, 2766769 rows affected (4 min 47.57 sec). PostgreSQL 마이그레이션 실행 시간: 58.11 sec, DELETE 2766690.

v7.8.5에서 OAuth 2 관련 수정을 백포트했습니다. 쿼리 시간은 삭제할 행이 있는지 여부에 따라 달라집니다.

삭제할 행이 없는 경우:

  • MySQL v5.7.12: 9ms
  • PostgreSQL v10: 21ms

4행인 경우:

  • MySQL v5.7.12: 17.2ms
  • PostgreSQL v10: 9.9ms

위 시간은 다음 테이블 크기를 기준으로 측정한 값입니다.

  • Preferences: 200만 레코드
  • oauthaccessdata와 sessions: 10레코드

다음을 실행하면 영향을 받는 행의 수를 확인할 수 있습니다.

PostgreSQL:

SELECT count(o.*)
 FROM oauthaccessdata o
 WHERE NOT EXISTS (
    SELECT p.*
    FROM preferences p
    WHERE o.clientid = p.name
      AND o.userid = p.
      userid
      AND p.category =
      'oauth_app'
    );

MySQL:

SELECT COUNT(o.`Token`)
FROM OAuthAccessData o
LEFT JOIN Preferences p ON o.
  clientid = p.name
  AND o.userid = p.userid
  AND p.category = 'oauth_app'
INNER JOIN Sessions s ON o.token = s
  .token
WHERE p.name IS NULL;

oauthaccessdata와 sessions 테이블에 대한 잠금은 삭제할 행이 있을 때만 획득됩니다.

v7.8.7에서, 사이트 URL의 출처가 아닌 다른 출처에서 웹소켓 연결을 허용하려는 서버는 ServiceSettings.AllowCorsFrom 설정을 지정하세요. 아울러 siteURL이 올바르게 설정되어 있는지도 확인하세요.

v7.8.11에서 데이터 보존 DeleteOrphanedRows 쿼리의 성능을 개선했습니다.

새 테이블을 위한 새 마이그레이션:

MySQL:

CREATE TABLE

IF NOT EXISTS
  RetentionIdsForDeletion(Id
    VARCHAR(26) NOT NULL,
    TableName VARCHAR(64),
    Ids json, PRIMARY KEY (Id
      ), KEY
    idx_retentionidsfordeletion_tablename
    (TableName)) ENGINE =
  InnoDB DEFAULT CHARSET =
  utf8mb4;
  ``

PostgreSQL:

CREATE TABLE

IF NOT EXISTS
  retentionidsfordeletion(id
    VARCHAR(26) PRIMARY KEY,
    tablename VARCHAR(64),
    ids VARCHAR(26) []);
  CREATE INDEX

IF NOT EXISTS
  idx_retentionidsfordeletion_tablename
  ON retentionidsfordeletion(
    tablename);
  ``

사용자나 채널을 완전 삭제하면 이제 연결된 리액션도 함께 정리됩니다.

기능 플래그 DataRetentionConcurrencyEnabled를 제거했습니다. 데이터 보존은 성능 저하를 피하기 위해 이제 동시성 없이 실행됩니다.

고아 리액션을 삭제할 때 한 번에 가져올 ID 배치 수를 관리자가 설정할 수 있는 새 설정 DataRetentionSettings.RetentionIdsBatchSize를 추가했습니다. 기본값은 100입니다.

v7.7

웹 앱 구성 요소를 가진 플러그인은 Mattermost v7.7 릴리스와 갱신된 React v17 의존성에 맞춰 수정이 필요할 수 있습니다.

이는 언마운트된 컴포넌트에서 findDOMNode가 호출되었다는 오류로 플러그인이 중단되는 것을 막기 위한 것입니다. 스타터 템플릿은 외부 버전의 React에 의존했지만 ReactDOM에 대해서는 그렇게 하지 않았습니다. 플러그인은 ReactDOM을 외부화하도록 webpack.config.js 지시문을 갱신해야 합니다. 참고로 https://github.com/mattermost/mattermost-plugin-playbooks/pull/1489 를 확인하세요. 서버 측에서만 동작하는 플러그인은 영향을 받지 않습니다. 이 변경은 Mattermost v7.7로 업그레이드하기 전 언제든지 기존 플러그인에 적용할 수 있으며 이전 버전의 Mattermost와도 하위 호환됩니다. 문제가 발생하면 ExperimentalSettings.PatchPluginsReactDOM을 활성화하거나, 해당 플러그인이 갱신될 때까지 비활성화하면 됩니다.

ThreadTeamId 컬럼을 추가해 Threads 테이블을 비정규화했습니다. 스키마가 비정규화되기는 하지만 불필요한 조인을 제거함으로써 성능이 향상됩니다.

스키마 변경에 대한 테스트 결과는 아래와 같습니다.

인스턴스: db.r5.large

Threads 테이블 크기: 846313행

게시물 수: 1,200만 건

리액션 수: 250만 건

MySQL:

-- Drop any existing TeamId column from 000094_threads_teamid.up.sql

SET @preparedStatement = (SELECT IF(
    EXISTS(``
        SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS
        WHERE table_name = 'Threads'
        AND table_schema = DATABASE()
        AND column_name = 'TeamId'
    ),
    'ALTER TABLE Threads DROP COLUMN TeamId;',
    'SELECT 1;'
));

PREPARE removeColumnIfExists FROM @preparedStatement;
EXECUTE removeColumnIfExists;
DEALLOCATE PREPARE removeColumnIfExists;

SET @preparedStatement = (SELECT IF(
    NOT EXISTS(
        SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
        WHERE table_name = 'Threads'
        AND table_schema = DATABASE()
        AND column_name = 'ThreadTeamId'

    ),
    'ALTER TABLE Threads ADD COLUMN ThreadTeamId varchar(26) DEFAULT NULL;',
    'SELECT 1;'
));

PREPARE addColumnIfNotExists FROM @preparedStatement;
EXECUTE addColumnIfNotExists;
DEALLOCATE PREPARE addColumnIfNotExists;

Query OK, 0 rows affected (7.71 sec)

UPDATE Threads, Channels
SET Threads.ThreadTeamId = Channels.TeamId
WHERE Channels.Id = Threads.ChannelId
AND Threads.ThreadTeamId IS NULL;

Query OK, 846313 rows affected (51.32 sec)

Rows matched: 846313 Changed: 846313 Warnings: 0

PostgreSQL:

-- Drop any existing TeamId column from 000094_threads_teamid.up.sql

ALTER TABLE threads DROP COLUMN IF EXISTS teamid;

ALTER TABLE threads ADD COLUMN IF NOT EXISTS threadteamid VARCHAR(26);

ALTER TABLE

Time: 2.236 ms

UPDATE threads
SET threadteamid = channels.
  teamid
FROM channels
WHERE threads.threadteamid IS
  NULL
  AND channels.id = threads.
  channelid;

UPDATE 847646

Time: 29744.608 ms (00:29.745)

**Backwards-compatibility:**

A previous version of Mattermost can run with the new schema changes

**Table locks or impact to existing operations on the table:**

Table locks - Threads table

위에 제시한 쿼리는 MySQL과 PostgreSQL 모두 Mattermost를 업그레이드하기 전에 실행할 수 있습니다. 스키마를 미리 변경해 두면 마이그레이션이 즉시(0.78초) 끝납니다.

v7.7과 함께 제공되는 Calls 버전부터 외부 RTCD 서비스를 사용할 때 최소 버전 요구 사항이 생겼습니다. 즉, Calls가 외부 서비스를 사용하도록 설정되어 있다면 RTCD를 먼저 최소 0.8.0 이상으로 업그레이드해야 하며, 그렇지 않으면 플러그인이 기동에 실패합니다.

v7.7.2에서 Message Priority & Acknowledgement가 이제 모든 인스턴스에서 기본적으로 활성화됩니다. 이 기능은 시스템 콘솔의 Posts > Message Priority에서 또는 설정의 PostPriority 항목으로 비활성화할 수 있습니다.

v7.5

Posts 테이블에서 ParentId 컬럼이 확실히 삭제되도록 새 스키마 마이그레이션을 추가했습니다. 컬럼이 이전에 삭제되지 않았다면 테이블 크기에 따라 MySQL 데이터베이스에서 CPU 사용량이 크게 치솟을 수 있습니다. 마이그레이션 중에는 테이블에 대한 쓰기가 제한됩니다.

PluginRegistry.registerCustomRoute로 사용자 지정 라우트 컴포넌트를 등록할 때는 루트 레이아웃에 올바르게 배치되도록 CSS grid-area를 반드시 지정해야 합니다(권장값: grid-area: center).

v7.3

Boards가 채널 기반 권한 시스템에서 역할 기반 권한 시스템으로 전환됩니다. 마이그레이션은 자동으로 진행되지만 관리자는 업그레이드 전에 백업을 수행해야 합니다. 워크스페이스가 제거되었으므로 마이그레이션 전에 여러 보드의 멤버였다면 이제 모든 보드가 같은 사이드바 아래에 표시됩니다.

v7.2

여러 스키마 변경으로 데이터를 더 엄격하게 다루기 위한 데이터베이스 제약이 추가되었습니다. 아래 나열된 명령은 모두 8코어·16GB RAM 장비에서 테스트했습니다. 측정된 시간은 다음과 같습니다.

PostgreSQL(채널 131869개, 팀 2개):

  • CREATE TYPE channel_type AS ENUM ('P', 'G', 'O', 'D'); — 14.114밀리초 소요
  • ALTER TABLE channels alter column type type channel_type using type::channel_type; — 3856.790밀리초(3.857초) 소요
  • CREATE TYPE team_type AS ENUM ('I', 'O'); — 4.191밀리초 소요
  • ALTER TABLE teams alter column type type team_type using type::team_type; — 116.205밀리초 소요
  • CREATE TYPE upload_session_type AS ENUM ('attachment', 'import'); — 4.266밀리초 소요
  • ALTER TABLE uploadsessions alter column type type upload_session_type using type::upload_session_type; — 37.099밀리초 소요

MySQL(채널 270959개, 팀 2개):

  • ALTER TABLE Channels MODIFY COLUMN Type ENUM("D", "O", "G", "P"); — 13.24초 소요
  • ALTER TABLE Teams MODIFY COLUMN Type ENUM("I", "O"); — 0.04초 소요
  • ALTER TABLE UploadSessions MODIFY COLUMN Type ENUM("attachment", "import"); — 0.03초 소요
v7.1

새 설정 항목 MaxImageDecoderConcurrency는 한 번에 동시에 디코딩할 수 있는 이미지 수를 지정합니다. 기본값은 -1이며 이 값은 시스템의 CPU 수를 의미합니다. 이 설정은 서버의 전체 메모리 사용량에 영향을 줍니다. 이미지 한 장의 최대 메모리는 MaxImageResolution * 24 bytes로 결정됩니다. 따라서 MaxImageResolution * MaxImageDecoderConcurrency * 24가 이미지 디코딩에 할당된 메모리보다 작도록 설정하는 것을 권장합니다.

Mattermost v7.1은 새 컬럼과 그 인덱스 형태의 스키마 변경을 도입합니다. 스키마 변경에 대한 테스트 결과는 다음과 같습니다.

  • MySQL, 게시물 1,200만 건·리액션 250만 건 - 약 1분 34초(인스턴스: 8코어·16GB RAM PC)
  • PostgreSQL, 게시물 1,200만 건·리액션 250만 건 - 약 1분 18초(인스턴스: db.r5.2xlarge)

업그레이드 전에 다음 SQL 쿼리를 실행하면 Reactions 테이블에 잠금을 획득할 수 있으며, 그 사이 사용자가 남긴 리액션은 마이그레이션이 끝날 때까지 데이터베이스에 반영되지 않습니다. 이 방식은 완전히 하위 호환됩니다.

MySQL:

  • ALTER TABLE Reactions ADD COLUMN ChannelId varchar(26) NOT NULL DEFAULT "";
  • UPDATE Reactions SET ChannelId = COALESCE((select ChannelId from Posts where Posts.Id = Reactions.PostId), '') WHERE ChannelId="";
  • CREATE INDEX idx_reactions_channel_id ON Reactions(ChannelId) LOCK=NONE;

PostgreSQL:

  • ALTER TABLE reactions ADD COLUMN IF NOT EXISTS channelid varchar(26) NOT NULL DEFAULT '';
  • UPDATE reactions SET channelid = COALESCE((select channelid from posts where posts.id = reactions.postid), '') WHERE channelid='';
  • CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_reactions_channel_id on reactions (channelid);
v7.0

IMPORTANT: 세션 길이 설정 항목의 단위가 *days*에서 *hours*로 변경되었습니다. config.json 파일이나 데이터베이스 설정으로 다음 값을 관리하는 인스턴스는 새 단위로 자동 마이그레이션되지만, 환경 변수를 사용하는 인스턴스는 다음과 같이 직접 변경해야 합니다.

  1. MM_SERVICESETTINGS_SESSIONLENGTHWEBINDAYS를 MM_SERVICESETTINGS_SESSIONLENGTHWEBINHOURS로 교체합니다(값은 24배).
  2. MM_SERVICESETTINGS_SESSIONLENGTHMOBILEINDAYS를 MM_SERVICESETTINGS_SESSIONLENGTHMOBILEINHOURS로 교체합니다(값은 24배).
  3. MM_SERVICESETTINGS_SESSIONLENGTHSSOINDAYS를 MM_SERVICESETTINGS_SESSIONLENGTHSSOINHOURS로 교체합니다(값은 24배).

MySQL을 사용하는 자체 호스팅 고객은 FileInfo 테이블의 행 수가 많을 경우 마이그레이션이 평소보다 오래 걸릴 수 있습니다. MySQL에서는 70만 행 규모의 테이블에 약 19초가 걸립니다. PostgreSQL에서 필요한 시간은 무시할 수 있는 수준입니다. 테스트는 CPU - Intel i7 6-cores @ 2.6 GHz, Memory - 16 GB 사양의 장비에서 수행했습니다.

System Console > Experimental > Features > Enable App Bar의 새 설정을 활성화하면, 플러그인이 등록한 모든 채널 헤더 아이콘이 새 Apps Bar로 이동합니다. 해당 위치에 컴포넌트를 렌더링하는 새 등록 함수를 명시적으로 사용하지 않은 경우에도 마찬가지입니다. 자체 호스팅 배포 환경에서 Apps Bar 설정의 기본값은 false입니다.

이제부터 ServiceSettings.TrustedProxyIPHeader의 값은 기본적으로 비어 있습니다. 이전 버그로 인해 특정 조건에서는 이렇게 동작하지 않았습니다. 고객은 설정에서 이 값을 확인하고 필요하다면 nil로 지정하시기 바랍니다. 자세한 내용은 여기를 참고하세요.

Collapsed Reply Threads가 이제 정식 출시되었으며 새 Mattermost 서버에서 기본적으로 활성화됩니다. v7.0 이상으로 업그레이드하는 서버라면 기능 활성화에 관한 자세한 내용과 안내를 이 문서에서 확인하세요.

v6.7

새 인덱스 형태의 스키마 변경이 도입되었습니다. 이 스키마 변경으로 데이터베이스 쿼리가 실행되는 데 걸린 시간을 측정한 테스트 결과는 다음과 같습니다.

MySQL, 게시물 700만 건 - 약 17초(인스턴스: db.r5.xlarge)

MySQL, 게시물 900만 건 - 2분 12초(인스턴스: db.r5.large)

Postgres, 게시물 700만 건 - 약 9초(인스턴스: db.r5.xlarge)

무중단 업그레이드를 원하는 고객은 업그레이드 전에 이 인덱스를 미리 적용하는 것을 권장합니다. 이 작업은 완전히 하위 호환되며 수동으로 실행할 경우 테이블 잠금을 획득하거나 테이블에 대한 기존 작업에 영향을 주지 않습니다. 미리 적용하지 않으면 업그레이드 과정에서 쿼리가 실행되며 MySQL이 아닌 환경에서는 테이블이 잠깁니다. 이 인덱스를 적용하려면 다음을 실행하세요.

MySQL: CREATE INDEX idx_posts_create_at_id on Posts(CreateAt, Id) LOCK=NONE;

Postgres: CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_posts_create_at_id on posts(createat, id);

v6.7.1부터 ServiceSettings.TrustedProxyIPHeader의 값은 기본적으로 비어 있습니다. 이전 버그로 인해 특정 조건에서는 이렇게 동작하지 않았습니다. 고객은 설정에서 이 값을 확인하고 필요하다면 nil로 지정하시기 바랍니다. 자세한 내용은 여기를 참고하세요.

v6.6

Apps Framework의 바인딩/폼 제출 프로토콜이 변경되어, 단일 call이 별도의 submit, form, refresh, lookup 호출로 분리되었습니다. 직접 앱을 만든 사용자는 새 방식에 맞게 수정해야 합니다.

이제 채널 관리자는 코드를 작성하지 않고도 트리거 조건에 따라 자동으로 실행되는 특정 동작을 설정할 수 있습니다. 구버전 Playbooks를 사용 중인 사용자는 채널 동작 기능을 사용하려면 Playbooks 인스턴스를 최소 v1.26 이상으로 업그레이드해야 합니다.

v6.6.2부터 ServiceSettings.TrustedProxyIPHeader의 값은 기본적으로 비어 있습니다. 이전 버그로 인해 특정 조건에서는 이렇게 동작하지 않았습니다. 고객은 설정에서 이 값을 확인하고 필요하다면 nil로 지정하시기 바랍니다. 자세한 내용은 여기를 참고하세요.

v6.5

mattermost version CLI 명령은 더 이상 데이터베이스와 상호작용하지 않습니다. 따라서 데이터베이스 버전은 출력되지 않습니다. 또한 version 하위 명령으로는 데이터베이스 마이그레이션이 적용되지 않습니다. 관리자가 마이그레이션을 직접 실행할 수 있도록 새 db migrate 하위 명령이 추가되었습니다.

v6.5.2부터 ServiceSettings.TrustedProxyIPHeader의 값은 기본적으로 비어 있습니다. 이전 버그로 인해 특정 조건에서는 이렇게 동작하지 않았습니다. 고객은 설정에서 이 값을 확인하고 필요하다면 nil로 지정하시기 바랍니다. 자세한 내용은 여기를 참고하세요.

v6.4

새 스키마 마이그레이션 시스템이 도입되었으므로, 서버를 이 버전으로 갱신하기 전에 데이터베이스를 백업할 것을 강력히 권장합니다. 새 마이그레이션 시스템은 기존 마이그레이션을 모두 훑어 새 테이블에 기록합니다. 이는 애플리케이션을 새 시스템으로 옮기기 위한 것으로 최초 실행 시에만 수행됩니다. 마이그레이션 정보가 저장되는 테이블의 이름은 db_migrations입니다. 또한 여러 설치 환경이 동시에 마이그레이션을 실행하지 못하도록 db_lock 테이블이 사용됩니다. 마이그레이션 적용 중 오류가 발생하면 이 테이블을 먼저 확인하세요. 다운타임은 데이터베이스의 레코드 수와 스키마에 누락된 마이그레이션이 있는지에 따라 달라집니다. 문제가 발생하면 실패한 마이그레이션 이름, 데이터베이스 드라이버/버전, 서버 로그를 포함해 이슈를 등록해 주세요.

MySQL에서 v6.4.0으로 업그레이드할 때 "Failed to apply database migrations" 오류가 발생한다면, 테이블 콜레이션과 데이터베이스 기본 콜레이션이 일치하지 않는다는 뜻입니다. ALTER DATABASE <YOUR_DB_NAME> COLLATE = 'utf8mb4_general_ci',로 데이터베이스 콜레이션을 변경해 직접 해결할 수 있습니다. 그런 다음 서버 업그레이드를 다시 수행하면 마이그레이션이 성공합니다.

MySQL 8 이상 시스템에서는 기본 콜레이션이 변경된 탓에 업그레이드 시 Error 1267: Illegal mix of collations 오류가 발생하는 경우가 흔히 관찰되었습니다. 이는 데이터베이스와 테이블의 콜레이션이 서로 다르기 때문입니다. 이 오류가 발생하면 예를 들어 ALTER DATABASE <db_name> COLLATE = '<collation>'로 콜레이션을 같은 값으로 맞추세요.

새 마이그레이션 시스템은 스키마 마이그레이션을 실행하기 위해 MySQL 데이터베이스 사용자에게 EXECUTE, CREATE ROUTINE, ALTER ROUTINE, REFERENCES 권한을 추가로 요구합니다.

v6.3

v6.3.3에서 ThreadAutoFollow의 기본값이 false로 변경되었습니다. 이 값이 이미 true로 지정된 기존 설정에는 영향을 주지 않습니다.

v6.3.9부터 ServiceSettings.TrustedProxyIPHeader의 값은 기본적으로 비어 있습니다. 이전 버그로 인해 특정 조건에서는 이렇게 동작하지 않았습니다. 고객은 설정에서 이 값을 확인하고 필요하다면 nil로 지정하시기 바랍니다. 자세한 내용은 여기를 참고하세요.

v6.2

채널 자동 완성 결과에 비공개 채널이 포함됩니다. 자동 완성에 Bleve나 Elasticsearch를 사용하는 고객은 새 결과를 얻기 위해 데이터를 다시 인덱싱해야 합니다. 이 작업은 오래 걸릴 수 있으므로 자동 완성을 비활성화한 뒤 백그라운드에서 인덱싱을 실행하는 것을 권장합니다. 인덱싱이 끝나면 자동 완성을 다시 활성화하세요.

참고

자동 완성 결과에서 비공개 채널 이름은 해당 채널의 멤버에게만 표시됩니다.

v6.2.3에서 ThreadAutoFollow의 기본값이 false로 변경되었습니다. 이 값이 이미 true로 지정된 기존 설정에는 영향을 주지 않습니다.

Mattermost Boards에서 실시간 업데이트가 올바르게 표시되려면 EnableReliableWebSockets 설정을 직접 true로 지정해야 합니다.

v6.1

v6.1로 업그레이드할 때는 스키마 마이그레이션 분석을 참고하세요.

Bleve 인덱스가 scorch 인덱스 타입을 사용하도록 갱신되었습니다. 새로 기본값이 된 이 인덱스 타입은 효율이 개선되어 인덱스가 차지하는 디스크 공간이 크게 줄어듭니다. 새 인덱스 타입을 사용하려면 서버 버전을 업그레이드한 뒤 시스템 콘솔의 Bleve 섹션에서 퍼지 작업을 실행하고 이어서 재인덱싱을 수행하세요. Bleve는 기존 인덱스와도 여전히 호환되므로 퍼지와 재인덱싱을 실행하지 않아도 현재 인덱싱된 데이터는 정상적으로 동작합니다.

쿼리 성능을 높이기 위해 jobs 테이블에 복합 인덱스를 추가했습니다. jobs 테이블이 큰 일부 고객 환경에서는 시간이 오래 걸릴 수 있으므로, 업무 시간 외에 인덱스를 먼저 추가한 다음 마이그레이션을 실행하는 것을 권장합니다. 행이 100만 개를 넘는 테이블이라면 업그레이드 전에 미리 처리할 만큼 크다고 볼 수 있습니다.

  • PostgreSQL: create index concurrently idx_jobs_status_type on jobs (status,type);
  • MySQL: create index idx_jobs_status_type on Jobs (Status,Type);

v6.1.3에서 ThreadAutoFollow의 기본값이 false로 변경되었습니다. 이 값이 이미 true로 지정된 기존 설정에는 영향을 주지 않습니다.

Mattermost Boards에서 실시간 업데이트가 올바르게 표시되려면 EnableReliableWebSockets 설정을 직접 true로 지정해야 합니다.

v6.0

마이그레이션 시간이 평소보다 길어질 수 있습니다.

  • 게시물 1,000만 건 이상 데이터셋의 예상 업그레이드 시간은 이 문서를 참고하세요.
  • 게시물 7,000만 건 이상 데이터셋의 예상 업그레이드 시간은 이 문서를 참고하세요.

model.ClusterMessage의 Data 필드 타입이 string에서 []byte로 변경되었습니다. v6 서버는 클러스터에서 v5 서버와 함께 실행할 수 없습니다. 5.x에서 6.x로 업그레이드하는 고객은 고가용성 업그레이드를 수행할 수 없습니다. 업그레이드 중 v6 서버를 기동할 때는 다른 v5 서버가 실행 중이지 않아야 합니다. v6 서버는 대규모 데이터베이스 스키마 변경을 수행하므로 데이터셋 크기에 따라 기동 시간이 크게 늘어날 수 있습니다. 무중단은 불가능하지만, 마이그레이션 과정에서 어떤 노력을 기울이느냐에 따라 다운타임을 상당 부분 줄일 수 있습니다.

  1. 적은 노력, 긴 다운타임 - v6 서버를 평범하게 기동하는 일반적인 절차입니다. 여기에는 두 가지 함의가 있습니다. 마이그레이션 과정에서 여러 테이블이 잠겨 그동안 해당 테이블이 읽기 전용이 됩니다. 그리고 서버가 마이그레이션을 마치고 애플리케이션을 시작하고 나면 클러스터 내에서 다른 v5 서버를 실행할 수 없습니다.
  2. 중간 노력, 중간 다운타임 - 이 방식은 서버에서 SQL 쿼리를 직접 실행해야 합니다. 테이블 잠금을 피하기 위해 MySQL에서는 pt-online-schema-change 도구를 사용할 수 있습니다. Postgres에서는 테이블 잠금이 매우 적습니다. 쿼리가 많기 때문에 고객이 업무 시간 외에 각 쿼리를 원하는 속도로 실행할 수 있다는 것이 장점입니다. #11을 제외한 모든 쿼리는 이런 방식으로 안전하게 실행할 수 있습니다. 그런 다음 (1)의 일반적인 방식을 따르면 됩니다.
  3. 많은 노력, 짧은 다운타임 - 이 방식은 (2)의 모든 작업에 더해 쿼리 #11의 영향을 최소화하려 합니다. 2단계를 수행한 뒤 v5 서버가 실행 중인 상태에서 v6를 함께 기동하고 애플리케이션 로그를 모니터링하면 됩니다. v6 애플리케이션이 기동되는 즉시 v5 노드 하나를 내려야 합니다. 이렇게 하면 다운타임을 몇 초 수준으로 줄일 수 있습니다.

마이그레이션 도중 서버 프로세스가 강제 종료되는 것을 피하려면 systemctl이 아니라 Mattermost를 직접 기동하는 것을 권장합니다. 제공되는 systemctl 서비스 파일의 TimeoutStartSec 값이 1시간으로 설정되어 있어 이런 일이 발생할 수 있습니다.

Mattermost Kubernetes 오퍼레이터를 사용하는 고객은 위 마이그레이션 정보를 숙지하고 자신에게 가장 적합한 경로를 선택해야 합니다. (1)을 감수할 수 있다면 오퍼레이터를 사용한 일반적인 업그레이드 절차로 충분합니다. 다운타임을 최소화하려면 일반적인 업그레이드 절차에 따라 오퍼레이터로 Mattermost를 갱신하기 전에 (2)를 먼저 수행하세요.

Mattermost 6.0.x 이상 버전으로 업그레이드할 때 다음 오류가 발생할 수 있습니다. Failed to alter column type. It is likely you have invalid JSON values in the column. Please fix the values manually and run the migration again.

이는 해당 컬럼에 직접 고쳐야 하는 잘못된 JSON 값이 들어 있다는 뜻입니다. 흔히 효과가 있는 것으로 알려진 해결책은 모든 \u0000 문자를 제거하는 것입니다.

다음 절차를 따르세요.

  1. 다음으로 영향을 받는 값을 확인합니다. SELECT COUNT(*) FROM <table> WHERE <column> LIKE '%\u0000%';
  2. 개수가 0보다 크면 해당 값들을 직접 확인하고 제거해도 되는지 판단합니다.
  3. UPDATE <table> SET <column> = regexp_replace(<column>, '\\u0000', '', 'g') where <column> like '%\u0000%';로 제거합니다.

그런 다음 Mattermost를 다시 기동해 보세요.

이 릴리스에서 사용이 중단된 항목 목록은 지원되지 않는 레거시 릴리스 문서를 참고하세요.

Focalboard 플러그인의 이름이 Mattermost Boards로 변경되었으며, v0.9.1(Mattermost v6.0과 함께 출시)이 이제 기본적으로 활성화됩니다.

고급 로깅 설정 스키마가 변경되었습니다. 이는 5.x 대비 호환성 파괴 변경입니다. 갱신된 문서를 참고하세요.

기존 테마 이름과 색상인 "Mattermost", "Organization", "Mattermost Dark", "Windows Dark"가 각각 새 테마 이름과 색상인 "Denim", "Quartz", "Indigo", "Onyx"로 갱신되었습니다. 기존 테마를 사용하던 사용자는 서버나 워크스페이스가 업그레이드된 뒤 테마 색상이 조금 달라진 것을 보게 됩니다. 기존 "Mattermost", "Organization", "Mattermost Dark", "Windows Dark" 테마의 테마 변수는 문서에서 계속 확인할 수 있습니다. 원한다면 이 테마 변수로 사용자 지정 테마를 만들 수 있습니다. 사용자 지정 테마는 이 변경의 영향을 받지 않습니다.

플러그인에 대한 호환성 파괴 변경이 일부 포함되어 있습니다.

  • 좌측 사이드바 전용 봇 아이콘 지원이 제거되었습니다.
  • 플러그인 매니페스트에서 사용이 중단된 "Backend" 필드를 제거했습니다.
  • 플러그인 매니페스트의 "Executables" 필드를 맵으로 변환했습니다.

Mattermost Boards에서 실시간 업데이트가 올바르게 표시되려면 EnableReliableWebSockets 설정을 직접 true로 지정해야 합니다.

v5.38.0

"config watcher"(config.json 파일을 자동으로 다시 읽어 들이는 메커니즘)가 제거되고 mmctl config reload 명령으로 대체되었습니다. 디스크에서 설정을 변경한 뒤에는 이 명령을 실행해야 변경이 적용됩니다. 이 변경으로 설정 처리 성능과 견고성이 개선되었습니다.

v5.38은 Collapsed Reply Threads(베타) 도입 이후 발생하던 스레드와 채널의 잘못된 멘션 개수 및 읽지 않음 표시 문제 일부를 수정합니다. 이 수정은 SQL 마이그레이션으로 이루어지며 대규모 데이터베이스에서는 완료까지 수 분이 걸릴 수 있습니다. fixCRTChannelMembershipCounts 수정은 채널 멤버십 약 400만 건과 채널 약 13만 개를 담은 데이터베이스에서 1분 20초가 걸립니다. fixCRTThreadCountsAndUnreads 수정은 스레드 56367개, 스레드 멤버십 124587건, 채널 멤버십 220801건을 담은 데이터베이스에서 약 3분 30초가 걸립니다. 이 수치는 MySQL v5.6.51 기준입니다.

Focalboard v0.8.2(Mattermost v5.38.0과 함께 출시)는 새 데이터베이스 연결 시스템 때문에 Mattermost v5.37 이상을 요구합니다.

v5.37.0

platform 바이너리와 “--platform” 플래그가 제거되었습니다. systemd 파일이나 사용자 지정 스크립트로 Mattermost 서버 애플리케이션을 실행하면서 “--platform” 플래그를 사용하거나 platform 바이너리를 직접 사용하고 있다면 이제 mattermost 바이너리만 사용해야 합니다.

Collapsed Reply Threads는 Mattermost 서버 v5.37 이상에서 베타로 제공됩니다. 기능을 안정화하는 과정이므로 버그를 겪을 수 있습니다. 특히 여기에 정리된 알려진 문제를 확인하세요.

v5.37은 이모지 표준 v13.0을 지원합니다. 과거에 새 시스템 이모지 이름과 같은 이름으로 사용자 지정 이모지를 추가했다면 해당 이모지는 시스템 이모지로 덮어써집니다. 사용자 지정 이모지의 이름을 변경하면 이 문제를 피할 수 있습니다.

Incident Collaboration의 일부 기능을 이제 모든 Mattermost 에디션에서 사용할 수 있습니다. 이번 갱신에 따라 Incident Collaboration은 최소 서버 버전으로 v5.37을 요구합니다. 각 에디션에서 제공되는 기능은 가격 페이지에서 확인하세요.

v5.37.8에서 ThreadAutoFollow의 기본값이 false로 변경되었습니다. 이 값이 이미 true로 지정된 기존 설정에는 영향을 주지 않습니다.

v5.36.0

Gossip 클러스터링 모드가 정식 출시되어 더 이상 선택 항목으로 제공되지 않습니다. 모든 클러스터 트래픽은 항상 gossip 프로토콜을 사용합니다. 설정 UseExperimentalGossip은 아무런 효과가 없으며 호환성 목적으로만 남아 있습니다. gossip 사용 여부를 지정하는 설정은 시스템 콘솔에서 제거되었습니다.

참고

고가용성 업그레이드에서는 클러스터의 모든 노드가 하나의 프로토콜을 사용해야 합니다. 기존 시스템이 현재 gossip을 사용하고 있지 않다면, 클러스터의 다른 노드가 구버전을 사용하는 상태에서 한 노드만 업그레이드할 수 없습니다. 고객은 고가용성 업그레이드에 gossip을 사용하거나, 모든 노드를 종료하고 업그레이드를 수행한 뒤 모든 노드를 다시 기동해야 합니다.

Focalboard를 활성화하려면 사이드바 메뉴에서 마켓플레이스를 열어 Focalboard 플러그인을 설치한 다음 Configure를 클릭해 활성화하고 저장하세요. NGINX 또는 Apache 웹 프록시 설정도 갱신하세요.

v5.35.0

곧 출시될 새 기능인 공유 채널과 Collapsed Reply Threads에 필요한 백엔드 데이터베이스 아키텍처가 도입되면서, v5.35 릴리스(2021년 5월 16일)의 마이그레이션 성능이 눈에 띄게 영향을 받았습니다. 데이터베이스의 크기·종류·버전에 따라 평소보다 긴 업그레이드 시간을 예상해야 합니다. 소요 시간은 수 분(일반적인 경우)에서 수 시간(최악의 경우, MySQL 5.x에 한함)까지 다양합니다. 이 과정에서 데이터베이스 CPU 사용량이 중간에서 상당한 수준까지 치솟을 수 있습니다. 마이그레이션의 성능 영향과 완화 방안에 대한 자세한 내용을 참고할 수 있습니다.

사용자 일괄 가져오기 과정에서 사용하던 기존 비밀번호 생성 로직은 상대적으로 취약했습니다. 따라서 관리자는 일괄 가져오기 과정에서 생성되었고 이후 한 번도 비밀번호를 변경하지 않은 모든 사용자의 비밀번호를 즉시 재설정하는 것이 좋습니다.

v5.35.0은 파일 검색이라는 새 기능을 도입합니다. 이미 데이터베이스에 있는 파일의 내용을 추출해 인덱싱하는 콘텐츠 추출 명령을 실행하기 전까지는 과거에 공유된 파일의 검색 결과가 불완전할 수 있습니다. Elasticsearch 또는 Bleve 검색 백엔드를 사용하는 인스턴스는 콘텐츠 추출이 끝난 뒤 일괄 인덱싱도 실행해야 합니다. 자세한 내용은 이 블로그 게시글을 참고하세요.

v5.34.1

v5.34.1은 v5.34.0으로 업그레이드할 때 실행되는 마이그레이션이 MySQL 설치 환경에서 타임아웃을 일으킬 수 있는 문제를 수정합니다. v5.34.1로 업그레이드하면 v5.32.0에 예정되어 있던 누락된 마이그레이션도 함께 실행될 수 있습니다. 이 추가 작업은 매우 큰 MySQL(5.x 버전) 설치 환경에서는 오래 걸릴 수 있습니다.

  • Posts.FileIds 타입 변경(PostgreSQL 전용)
  • 새 컬럼 ThreadMemberships.UnreadMentions 추가
  • 새 컬럼 Channels.Shared 추가
  • 새 컬럼 Reactions.UpdateAt 추가
  • 새 컬럼 Reactions.DeleteAt 추가
v5.33.0

리액션 삭제는 이제 Reactions 테이블에서 소프트 삭제로 처리됩니다. 스키마 갱신이 필요하며, 데이터가 많은 환경에서는 첫 실행 시 최대 15초가 걸릴 수 있습니다.

HTTP 1.1 미만 버전으로 수행된 WebSocket 핸드셰이크는 경고를 발생시키며, 서버는 WebSocket RFC를 준수하기 위해 버전을 1.1로 투명하게 올립니다. 이는 proxy_http_version 지시문을 1.1로 설정하지 않은 잘못된 Nginx(및 기타 프록시) 설정을 우회하기 위한 것입니다. 이 보완 장치는 향후 Mattermost 버전에서 제거될 예정이므로 WebSocket 프로토콜을 올바르게 사용하도록 프록시 설정을 수정할 것을 강력히 권장합니다.

v5.32.0

ExperimentalChannelOrganization, EnableXToLeaveChannelsFromLHS, CloseUnusedDirectMessages, ExperimentalHideTownSquareinLHS 설정은 새 사이드바 환경과 호환되지 않으므로 레거시 사이드바(EnableLegacySidebar)가 활성화된 경우에만 동작합니다. 새 사이드바가 이제 모든 사용자에게 적용되었으므로 ExperimentalChannelSidebarOrganization은 사용이 중단되었습니다.

Golang 클라이언트 API에 호환성 파괴 변경이 도입되었습니다. GetPostThread, GetPostsForChannel, GetPostsSince, GetPostsAfter, GetPostsBefore, GetPostsAroundLastUnread는 이제 collapsedThreads 파라미터를 추가로 전달받습니다. 이 함수들을 사용하는 클라이언트는 의존성을 갱신할 때 함께 수정해야 합니다.

Go 버전을 v1.15.5로 올리면서 호환성 파괴 변경이 도입되어, LDAP 서버 인증서에 주체 대체 이름(SAN)이 없으면 AD/LDAP 동기화 시 사용자 로그인이 실패합니다. AD/LDAP 서버에서 SAN이 포함된 인증서를 새로 만들면 해결됩니다.

TLS 1.0과 1.1 버전은 브라우저 공급업체에서 지원을 중단했습니다. Mattermost 서버 v5.32(2월 16일)부터 mmctl은 이 TLS 버전으로 배포된 Mattermost 서버에 연결할 때 오류를 반환합니다. 시스템 관리자가 계속 사용하려면 명령에 플래그를 명시적으로 추가해야 합니다. TLS 1.2 이상으로 업그레이드하는 것을 권장합니다.

v5.31.0

모바일 앱 v1.42.0 이상에서는 최소 서버 버전이 v5.31.3으로 지정되었습니다. v5.31.3에서 서버 버전이 v5.30.0으로 잘못 보고되던 문제가 수정되었기 때문입니다.

v5.29.0

2021년 1분기에 베타로 출시되는 Collapsed Reply Threads를 지원하기 위해 새 설정 ThreadAutoFollow가 추가되었습니다. 이 설정은 기본적으로 활성화되어 있으며 서버 성능에 영향을 줄 수 있습니다. 사용자 규모에 맞게 서버가 적절히 확장되어 있는지 확인하려면 하드웨어 요구 사항 문서를 검토하는 것을 권장합니다.

다시 활성화되어 개선된 SAML 라이브러리를 사용하도록 xmlsec1 기반 SAML 라이브러리를 비활성화했습니다.

v5.28.0

이제 서비스가 비정상 종료되면 스택 트레이스만 콘솔에 출력하는 대신 코어 덤프를 생성합니다. 이를 통해 디버깅에 도움이 되도록 비정상 종료 시점의 전체 정보를 보존할 수 있습니다.

코어 덤프에 관한 자세한 내용은 https://man7.org/linux/man-pages/man5/core.5.html 를 참고하세요.

새 서버 및 데스크톱 버전에서 제공되는 최신 제품 개선 사항을 시스템 관리자와 최종 사용자에게 알리기 위해 제품 내 공지가 도입되었습니다. 제품 내 공지와 이를 비활성화하는 방법은 문서에서 확인하세요.

다시 활성화되어 개선된 SAML 라이브러리를 사용하도록 xmlsec1 기반 SAML 라이브러리를 비활성화했습니다.

v5.27.0

다시 활성화되어 개선된 SAML 라이브러리를 사용하도록 xmlsec1 기반 SAML 라이브러리를 비활성화했습니다.

v5.26.0

v5.26에서는 Elasticsearch 인덱스를 다시 만들어야 했습니다. 관리자는 모든 변경 사항이 인덱스에 반영되도록 Purge index를 실행한 뒤 Index now 버튼으로 Elasticsearch를 다시 인덱싱해야 합니다. 인덱싱 중에는 검색이 제한될 수 있고 수 시간이 걸릴 수도 있으므로, 활동이 거의 없는 시간대에 수행하세요.

ClusterSettings 아래에 EnableExperimentalGossipEncryption 옵션이 추가되었습니다. 이 값을 true로 설정하고 UseExperimentalGossip도 true이면 gossip 프로토콜을 사용하는 모든 클러스터 통신이 암호화됩니다. 암호화는 기본적으로 AES-256을 사용하며 설계상 설정으로 변경할 수 없습니다. 다만 원한다면 Systems 테이블의 ClusterEncryptionKey 행에 값을 직접 지정할 수 있습니다. 키는 base64로 변환된 바이트 배열이며, AES-128, AES-192, AES-256 중 하나를 선택하려면 각각 16, 24, 32바이트여야 합니다.

키를 갱신하려면 MySQL에서는 UPDATE Systems SET Value='<value>' WHERE Name='ClusterEncryptionKey';를, PostgreSQL에서는 UPDATE systems SET value='<value>' WHERE name='ClusterEncryptionKey'를 실행하면 됩니다.

이 설정 변경을 적용하려면 먼저 클러스터 전체를 종료해야 합니다. 그런 다음 설정을 변경하고 재시작합니다. 클러스터 내 모든 서버는 암호화를 전부 사용하거나 전혀 사용하지 않아야 하며, 일부만 사용할 수는 없습니다.

SAML 설정 "Use Improved SAML Library (Beta)"가 강제로 비활성화되었습니다.

PostgreSQL은 2020년 2월에 9.4 버전의 장기 지원을 종료했습니다. PostgreSQL 9.4가 더 이상 지원되지 않으므로 Mattermost는 v5.26부터 PostgreSQL 10 버전을 공식 지원합니다. 신규 설치에는 PostgreSQL 10 이상이 필요합니다. 현재 ESR을 포함한 이전 Mattermost 버전은 PostgreSQL 9.4와 계속 호환됩니다. PostgreSQL 9.4를 비롯한 모든 9.x 버전은 v5.30 릴리스(2020년 12월 16일)에서 완전히 사용 중단되었습니다. PostgreSQL 문서의 업그레이드 절을 따르세요.

v5.25.0

Active Directory ADFS로 SAML을 구성할 때 “Relying Party Trust Identifier”를 설정하는 방법에 관한 잘못된 안내가 일부 수정되었습니다. 기존 설정도 계속 동작하지만 해당 설정을 수정하는 것을 권장합니다.

다시 활성화되어 개선된 SAML 라이브러리를 사용하도록 xmlsec1 기반 SAML 라이브러리를 비활성화했습니다.

v5.24.0

새 설정 ExtendSessionLengthWithActivity는 사용자가 Mattermost 앱에서 활동 중이면 세션을 자동으로 연장해 로그인 상태를 유지합니다. 조직 정책에 부합한다면 사용자 경험을 개선하기 위해 이 설정을 활성화하는 것을 권장합니다. 자세한 내용은 여기를 참고하세요.

(Enterprise Edition의) mattermost_http_request_duration_seconds 히스토그램 메트릭이 제거되었습니다. 이 정보는 API 핸들러 이름, HTTP 메서드, 응답 코드까지 함께 담고 있는 mattermost_api_time에 이미 수집되고 있었습니다.

예를 들어 평균 호출 시간을 측정하기 위해 rate(mattermost_http_request_duration_seconds_sum{server=~"$var"}[5m]) / rate(mattermost_http_request_duration_seconds_count{server=~"$var"}[5m])를 사용하고 있다면 sum(rate(mattermost_api_time_sum{server=~"$var"}[5m])) by (instance) / sum(rate(mattermost_api_time_count{server=~"$var"}[5m])) by (instance)로 교체해야 합니다.

이모지 리액션과 관련된 성능 문제를 수정하면서, 데이터베이스에 리액션이 많은 환경에서는 스키마 업그레이드에 시간이 더 걸리게 되어 업그레이드 성능이 영향을 받았습니다. 이런 환경에서는 사용량이 적은 시간대에, 가능하면 업그레이드보다 앞서 스키마 마이그레이션을 수행하는 것을 권장합니다. 이 마이그레이션은 Mattermost 서버가 완전히 기동되기 전에 실행되므로, 고가용성 구성이 아닌 설치 환경에서는 그동안 접속할 수 없습니다.

이 마이그레이션은 SQL 한 줄이므로 Mattermost 서버 재시작과 분리하고 싶다면 MySQL/PSQL 명령줄 클라이언트로 데이터베이스에 직접 적용할 수 있습니다. 완전히 하위 호환되므로 이전 버전의 Mattermost에도 문제 없이 스키마 변경을 적용할 수 있습니다. 스키마 변경이 실행되는 동안(Reactions 테이블 100만 행당 약 30초) 최종 사용자가 게시물에 리액션을 남기려 하면 이모지 리액션이 로드되지 않습니다.

MySQL: ALTER TABLE Reactions DROP PRIMARY KEY, ADD PRIMARY KEY (PostId, UserId, EmojiName);

PostgreSQL: ALTER TABLE reactions DROP CONSTRAINT reactions_pkey, ADD PRIMARY KEY (PostId, UserId, EmojiName);

모바일 앱에서는 자동 완성 드롭다운에 LDAP 그룹 멘션(E20 기능)이 표시되지 않습니다. LDAP 그룹에 속한 사용자는 알림은 계속 받지만 그룹 멘션 키워드가 강조 표시되지는 않습니다.

SAML 설정 "Use Improved SAML Library (Beta)"가 강제로 비활성화되었습니다. 기능적으로 동등한 xmlsec1 유틸리티로 SAML을 활성화하는 방법은 https://docs.mattermost.com/administration-guide/onboard/sso-saml.html 의 안내를 따르세요.

v5.22.0

이모지 리액션과 관련된 성능 문제를 수정하면서, 데이터베이스에 리액션이 많은 환경에서는 스키마 업그레이드에 시간이 더 걸리게 되어 업그레이드 성능이 영향을 받았습니다. 이런 환경에서는 사용량이 적은 시간대에, 가능하면 업그레이드보다 앞서 스키마 마이그레이션을 수행하는 것을 권장합니다. 이 마이그레이션은 Mattermost 서버가 완전히 기동되기 전에 실행되므로, 고가용성 구성이 아닌 설치 환경에서는 그동안 접속할 수 없습니다.

이 마이그레이션은 SQL 한 줄이므로 Mattermost 서버 재시작과 분리하고 싶다면 MySQL/PSQL 명령줄 클라이언트로 데이터베이스에 직접 적용할 수 있습니다. 완전히 하위 호환되므로 이전 버전의 Mattermost에도 문제 없이 스키마 변경을 적용할 수 있습니다. 스키마 변경이 실행되는 동안(Reactions 테이블 100만 행당 약 30초) 최종 사용자가 게시물에 리액션을 남기려 하면 이모지 리액션이 로드되지 않습니다.

MySQL: ALTER TABLE Reactions DROP PRIMARY KEY, ADD PRIMARY KEY (PostId, UserId, EmojiName);

Postgres: ALTER TABLE reactions DROP CONSTRAINT reactions_pkey, ADD PRIMARY KEY (PostId, UserId, EmojiName);

채널 조정 설정 기능은 모바일 앱 v1.30 이상에서 지원됩니다. 그 이전 버전의 모바일 앱에서는 적절한 권한 없이 게시물을 작성하거나 리액션을 남기려는 사용자에게 오류가 표시됩니다.

model.Post 구조체의 Props 필드에 직접 접근하는 방식은 사용이 중단되었습니다. 이제 제공되는 GetProps()와 SetProps() 메서드를 사용해야 합니다. 또한 model.Post 구조체를 직접 복사하지 말고 제공되는 Clone() 메서드를 사용해야 합니다.

SAML 설정 "Use Improved SAML Library (Beta)"가 강제로 비활성화되었습니다. 기능적으로 동등한 xmlsec1 유틸리티로 SAML을 활성화하는 방법은 https://docs.mattermost.com/administration-guide/onboard/sso-saml.html 의 안내를 따르세요.

v5.21.0

KVCompareAndSet, KVCompareAndDelete, KVList에서 키-값 만료를 준수하도록 했습니다. 또한 Postgres에서 플러그인 키-값 경합 상황과 삭제된 키의 처리를 개선했습니다.

SAML 설정 "Use Improved SAML Library (Beta)"가 강제로 비활성화되었습니다. 기능적으로 동등한 xmlsec1 유틸리티로 SAML을 활성화하는 방법은 https://docs.mattermost.com/administration-guide/onboard/sso-saml.html 의 안내를 따르세요.

v5.20.0

config.json에서 활성화되어 있지 않은 사전 패키징 플러그인은 더 이상 자동으로 설치되지 않지만 플러그인 마켓플레이스를 통해 계속 설치할 수 있습니다.

대화형 다이얼로그의 불리언 요소는 더 이상 문자열로 직렬화되지 않습니다. 호환성 파괴 변경은 되도록 피하려 하지만, v5.16에서 도입된 불리언 요소가 웹 앱과 모바일 앱 모두에서 동작하려면 이 변경이 불가피했습니다.

v5.19.0

LockTeammateNameDisplay 설정은 Team Edition과 Enterprise Edition E10에서 잘못 제공되고 있었으므로 Enterprise Edition E20으로 이동되었습니다.

v5.18.0

모바일 앱에서 게시물을 읽지 않음으로 표시하려면 v1.26 이상이 필요합니다. v5.18을 사용하더라도 모바일이 v1.25 이하라면, 웹 앱이나 데스크톱에서 게시물을 읽지 않음으로 표시한 결과가 모바일에서는 앱을 다시 실행하거나 포그라운드로 전환할 때에야 반영됩니다.

Go 모듈 버전 규격을 준수하기 위해 mattermost-server의 Go 모듈 경로가 변경되었습니다. mattermost-server를 의존성으로 사용하는 Go 모듈 개발자는 이 의존성을 v5.18로 올릴 때 모듈 경로와 import 경로를 github.com/mattermost/mattermost-server/v5로 변경해야 합니다. 자세한 내용은 https://go.dev/blog/v2-go-modules를 참고하세요.

관련 웹소켓 이벤트에서 Team.InviteId를 제거하고, 초대 권한이 없는 사용자에 대해서는 모든 팀 API 엔드포인트에서 이 값을 걸러내도록 했습니다.

PUT /channels/{channel_id} API 엔드포인트로 채널 유형을 변경하는 기능이 제거되었습니다. 이 용도로는 새 엔드포인트 PUT /channels/{channel_id}/privacy를 사용해야 합니다.

v5.16.0

Internet Explorer(IE11) 지원이 제거되었습니다. 자세한 내용은 이 포럼 게시글을 참고하세요.

Mattermost Desktop v4.3.0 릴리스에는 비보안 URL http://에서 데스크톱 알림을 보내는 방식에 대한 변경이 포함되어 있습니다. 비보안 http:// Mattermost 서버를 사용하는 조직은 Mattermost Desktop v4.3.0 이상에서 데스크톱 알림을 계속 받으려면 Mattermost 서버를 5.16.0 이상, 5.15.1, 5.14.4 또는 5.9.5(ESR)로 갱신해야 합니다.

게스트 계정을 활성화하면 사용자를 초대할 수 있는 모든 사용자가 기본적으로 게스트도 초대할 수 있게 됩니다. 시스템 관리자는 System Console > Permissions Schemes에서 각 역할의 이 권한을 제거해야 합니다. Mattermost 서버 5.17 버전에서는 시스템 관리자만 게스트 초대 권한을 자동으로 부여받게 되지만, 데이터베이스 마이그레이션 절차 때문에 이 수정은 5.16에는 적용되지 않습니다.

v5.14.0

이제 웹훅은 해당 웹훅을 만든 사용자나 시스템 관리자에게만 표시됩니다.

Google+에서 Google People로 전환됨에 따라 시스템 관리자는 갱신된 문서에 맞춰 config.json의 GoogleSettings.Scope 설정을 profile email로, UserAPIEndpoint 설정을 https://people.googleapis.com/v1/people/me?personFields=names,emailAddresses,nicknames,metadata로 지정해야 합니다.

v5.12.0

플러그인에서 DeleteEphemeralMessage 플러그인 API를 사용한다면 postId string 파라미터를 받도록 수정하세요. 자세한 내용은 문서를 참고하세요.

System Console > Enable Link Previews를 활성화하지 않으면 이미지 링크와 YouTube 미리 보기가 표시되지 않습니다. Mattermost 서버가 인터넷에 연결되어 있고 미리 보기가 표시되어야 할 웹사이트에 네트워크로 접근할 수 있는지 확인하세요. 자세한 내용은 여기를 참고하세요.

ExperimentalEnablePostMetadata 설정이 제거되었습니다. 게시물 크기를 포함한 게시물 메타데이터가 이제 데이터베이스에 저장되어, 채널에서 콘텐츠가 로드될 때 스크롤 위치가 올바르게 유지되고 스크롤이 튀는 현상이 사라집니다.

그룹 동기화로 팀·채널 관리자를 관리할 수 있는 기능이 추가되었습니다. 그룹 동기화가 활성화되어 있으면 업그레이드 시 모든 팀·채널 관리자 지정이 사라집니다. 업그레이드 전에 팀 및 채널 관리자를 해당 팀·채널에 대응하는 관리자 전용 LDAP 그룹에 추가해 둘 것을 강력히 권장합니다. 업그레이드 후에는 그 그룹들을 팀 또는 채널 관리자 역할과 역할 동기화해야 합니다.

v5.11.0

통합 기능에서 Props를 비우기 위해 Update.Props == nil을 사용하고 있다면 5.11 이상에서는 더 이상 동작하지 않습니다. 대신 Update.Props == {}를 사용해 속성을 비우세요.

이 변경은 Update.Props == nil이 프로필 사진 같은 값을 보존하지 않고 모든 Props를 의도치 않게 비워 버렸기 때문에 이루어졌습니다.

v5.10.0

config.json의 SupportedTimezonesPath 설정과 timezones.json 파일을 기반으로 UI의 시간대를 변경하는 기능이 제거되었습니다. 이는 데이터베이스에 설정 저장을 지원하기 위한 것입니다.

v5.9.0

config.json의 DisableLegacyMfa 설정이 true이고 다단계 인증이 활성화되어 있다면, 사용자가 모바일 앱을 1.17 이상으로 갱신했는지 확인하세요. 그렇지 않으면 MFA를 사용하는 사용자가 로그인에 실패할 수 있습니다.

config.json 파일에 이 설정이 정의되어 있지 않으면, 호환성 파괴를 피하기 위해 DisableLegacyMfa 설정은 기본적으로 false로 지정됩니다.

보안을 한층 강화하려면 DisableLegacyMfa를 true로 설정하는 것을 권장합니다.

보안을 한층 강화하기 위해, Open Graph 메타데이터·웹훅·슬래시 명령처럼 신뢰할 수 없는 외부 요청 맥락에서 Mattermost 애플리케이션 서버의 공인 IP는 예약된 IP로 간주됩니다. 자세한 내용은 문서를 참고하세요.

v5.8.0

로컬 이미지 프록시가 추가되어, 클라이언트에 표시되는 이미지도 이제 AllowUntrustedInternalConnections 설정의 영향을 받습니다. 이미지 로딩에 문제가 있다면 문서에서 자세한 내용을 확인하세요.

v5.6.0

내장 WebRTC가 제거되었습니다. 자세한 내용은 여기를 참고하세요.

config.json의 EnablePublicChannelsMaterialization 설정이 false라면, 채널 전환기(CTRL/CMD+K), 채널 자동 완성(~) 등 UI 전반의 채널 검색 성능을 높이기 위해 공개 채널용 구체화 테이블을 동기화하는 오프라인 마이그레이션을 업그레이드 전에 수행해야 할 수 있습니다. 다음 절차를 따르세요.

  1. 애플리케이션 서버를 종료합니다.
  2. Mattermost 데이터베이스에 접속합니다.
  3. 다음 쿼리를 실행합니다.
DELETE FROM PublicChannels;
INSERT INTO PublicChannels
    (Id, DeleteAt, TeamId, DisplayName, Name, Header, Purpose)
SELECT
    c.Id, c.DeleteAt, c.TeamId, c.DisplayName, c.Name, c.Header, c.Purpose
FROM
    Channels c
WHERE
    c.Type = 'O';

위 쿼리는 원본 데이터인 Channels 테이블은 그대로 두고 구체화된 PublicChannels 테이블만 재구성합니다.

참고

실험적 PublicChannels 기능을 한 번도 비활성화한 적이 없다면 이 마이그레이션은 필요하지 않습니다. 이 기능은 문제가 생겼을 때 비활성화할 수 있도록 임시 플래그와 함께 Mattermost v5.4에서 출시되었지만 실제로 그럴 일은 없었습니다. 이 설정을 변경한 적이 없다면 마이그레이션을 수행할 필요가 없습니다.

v5.4.0

Mattermost 모바일 앱 1.13 이상이 필요합니다. 그 이전 버전의 모바일 앱에서는 파일 업로드가 실패합니다.

특정 업그레이드 시나리오에서는 General 다음 Users and Teams 아래의 새 설정 Allow Team Administrators to edit others posts가 True로 지정될 수 있습니다. 5.1 이하와 신규 5.4 이상 설치 환경에서 Mattermost의 기본값은 False입니다.

v5.3.0

Elasticsearch가 활성화된 서버에서는 해시태그 검색이 대소문자를 구분합니다.

v5.2.0

v4.1~v4.4에서 v5.2 이상으로 바로 업그레이드하면서 Jira를 사용 중인 서버는 업그레이드 후 Jira 플러그인을 다시 활성화해야 합니다.

v5.1.0

mattermost export CLI 명령의 이름이 mattermost export schedule로 변경되었습니다. 이 명령을 사용하고 있다면 스크립트를 갱신하세요.

v5.0.0

모든 API v3 엔드포인트가 제거되었습니다. 통합 기능을 API v4로 이전하는 방법은 문서를 참고하세요.

설치와 업그레이드 과정을 명확히 하기 위해 platform 바이너리의 이름이 mattermost로 변경되었습니다. systemd 서비스 파일이 새 mattermost 바이너리를 가리키도록 수정해야 합니다. 일괄 로딩 도구와 개발자 도구를 포함한 모든 명령줄 도구의 이름도 platform에서 mattermost로 변경됩니다.

Account Settings > Notifications > Desktop Notifications에서 데스크톱 알림 지속 시간을 설정하던 Mattermost 사용자 설정이 제거되었습니다.

GET 요청을 받도록 구성된 슬래시 명령은 표준 HTTP 요청과 일관되게, 요청 본문 대신 쿼리 문자열에 페이로드가 인코딩되어 전달됩니다. 가능성은 낮지만 GET 요청을 잘못 사용하는 사용자 지정 슬래시 명령은 이로 인해 동작하지 않을 수 있습니다.

자동 링크를 적용할 프로토콜 종류를 허용 목록으로 지정하는 새 config.json 설정이 추가됩니다. Mattermost에서 사용자 지정 프로토콜의 자동 링크에 의존하고 있다면 업그레이드 전에 config.json에 해당 프로토콜을 허용 목록으로 등록하세요.

APIv4의 팀 영구 삭제 파라미터를 비활성화하는 새 config.json 설정이 추가되었습니다. 이 설정은 GitLab Omnibus로 배포된 환경을 제외한 모든 신규 및 기존 설치 환경에서 기본적으로 꺼져 있습니다. 이 APIv4 파라미터에 의존하고 있다면 업그레이드 전에 config.json에서 해당 설정을 활성화하세요.

사용되지 않던 ExtraUpdateAt 필드가 채널 모달에서 제거됩니다.

이 릴리스는 기본값인 4000자를 넘는 게시물 메시지를 지원하지만 데이터베이스 마이그레이션을 직접 수행해야 할 수 있습니다. 이 마이그레이션은 전적으로 선택 사항이며, 게시물 메시지를 최대 16383자까지 허용하고자 할 때만 수행하면 됩니다. 많은 설치 환경에서는 마이그레이션이 필요 없거나 기존 한도로도 충분합니다.

5.0.0으로 업그레이드한 뒤 현재 게시물 길이 한도를 확인하려면 기동 시 출력되는 다음 로그 메시지를 찾으세요.

[2018/03/27 09:08:00 EDT] [INFO] Post.Message supports at most 16383 characters (65535 bytes)

5.0.0 기준으로 게시물 메시지의 최대 길이는 16383(멀티바이트) 문자입니다. 로그에 이 한도보다 작은 값이 표시되고 더 긴 게시물 메시지를 사용하려면 아래 설명대로 데이터베이스를 직접 마이그레이션해야 합니다. Posts 테이블이 클수록 이 마이그레이션은 느려질 수 있으므로 사용량이 적은 시간대에 업그레이드를 계획하는 것이 좋습니다.

MySQL 데이터베이스를 마이그레이션하려면 데이터베이스에 접속해 다음을 실행하세요.

ALTER TABLE Posts MODIFY COLUMN Message TEXT;

PostgreSQL 데이터베이스를 마이그레이션하려면 데이터베이스에 접속해 다음을 실행하세요.

ALTER TABLE Posts ALTER COLUMN Message TYPE VARCHAR(65535);

Mattermost 인스턴스를 재시작합니다.

Enterprise E20 배포 환경에서는 팀 재정의 스킴을 사용하기 전에 config.json에서 RunJobs를 활성화하고 권한 마이그레이션이 완료되도록 해야 합니다.

v4.10.0

링크를 통해 팀에 다시 참여할 수 있던 버그를 수정하면서 기존 이메일 초대 링크는 더 이상 동작하지 않습니다. 팀 초대 링크 대화 상자에서 복사한 팀 초대 링크, 비밀번호 재설정 링크, 이메일 인증 링크는 영향을 받지 않으며 계속 유효합니다.

System Console > Logs에 기록되는 서버 로그와 System Console > Logging > File Log Directory에 지정된 mattermost.log 파일이 이제 JSON 형식을 사용합니다. 서버 로그를 파싱해 외부 시스템으로 보내는 도구를 만들어 두었다면 해당 도구가 JSON 형식을 지원하는지 확인하세요.

투명도가 있는 팀 아이콘은 팀 사이드바에서 흰색 배경으로 채워집니다.

SAML 인증이 활성화된 서버는 사용량이 적은 시간대에 업그레이드해야 합니다. 로그인 문제를 막기 위해 SAML 이메일 주소가 소문자로 마이그레이션되며, 이로 인해 업그레이드 시간이 평소보다 길어질 수 있습니다.

PostgreSQL 데이터베이스를 사용하고 비밀번호에 특수 문자(예: [])가 포함되어 있다면 비밀번호에서 해당 문자를 이스케이프하세요. 예를 들어 xxx[]xxx는 xxx%5B%5Dxxx가 됩니다.

v4.9.0

Docker로 Mattermost를 운영 환경에서 사용할 때의 편의를 높이기 위해, Docker 이미지가 이제 비루트 사용자로 실행되며 8000 포트를 사용합니다. 기존 설치 환경에 적용되는 중요한 변경 사항은 업그레이드 안내를 읽어 보세요.

여러 설정 항목이 데이터베이스의 역할로 이전되어 config.json 값을 변경해도 더 이상 적용되지 않습니다. 이 권한들은 이전과 마찬가지로 각각의 시스템 콘솔 설정에서 수정할 수 있습니다. 영향을 받는 config.json 설정은 다음과 같습니다.

  • RestrictPublicChannelManagement,
  • RestrictPrivateChannelManagement,
  • RestrictPublicChannelCreation,
  • RestrictPrivateChannelCreation,
  • RestrictPublicChannelDeletion,
  • RestrictPrivateChannelDeletion,
  • RestrictPrivateChannelManageMembers,
  • EnableTeamCreation,
  • EnableOnlyAdminIntegrations,
  • RestrictPostDelete,
  • AllowEditPost,
  • RestrictTeamInvite,
  • RestrictCustomEmojiCreation.

역할 기반 권한 시스템으로의 이전에 맞춰 config.json 설정 PostEditTimeLimit의 동작이 변경되었습니다. 게시물 수정이 허용된 경우 언제든 수정할 수 있게 하려면 "PostEditTimeLimit": -1로 지정하고, 수정 가능 시간을 초 단위로 제한하려면 "PostEditTimeLimit"에 양의 정수를 지정하세요. 게시물 수정이 비활성화되어 있으면 이 설정은 적용되지 않습니다.

프록시 서버 없이 Let's Encrypt를 사용한다면 config.json의 Forward80To443 설정을 true로 지정하지 않는 한 서버가 오류 메시지와 함께 기동에 실패합니다.

80 포트를 443으로 전달하는 경우, config.json의 ListenAddress 설정이 443 포트를 수신하도록 지정되어 있지 않으면 서버가 오류 메시지와 함께 기동에 실패합니다.

v4.6.2

프록시 서버 없이 Let's Encrypt를 사용한다면 Let's Encrypt 인증을 완료하기 위해 config.json의 Forward80To443 설정을 true로 지정하고 방화벽에서 80 포트를 전달하세요.

v4.4.0

Posts 테이블에 복합 데이터베이스 인덱스가 추가되었습니다. 메시지가 100만 건을 넘는 서버에서는 업그레이드 시간이 길어질 수 있습니다.

LDAP 동기화는 이제 이메일에 의존합니다. AD/LDAP 서버의 모든 사용자가 이메일 주소를 가지고 있는지, 아니면 해당 계정이 Mattermost에서 비활성화되어 있는지 확인하세요.

v4.2.0

Mattermost는 이제 일반 텍스트 콘텐츠 타입을 포함해 통합 기능의 여러 콘텐츠 타입을 처리합니다. 통합 기능이 생성된 메시지를 렌더링하지 않고 갑자기 JSON 페이로드 데이터를 그대로 출력한다면, 기존 동작을 유지하도록 통합 기능이 application/json content-type을 반환하는지 확인하세요.

기본적으로 Open Graph 메타데이터, 웹훅, 슬래시 명령에 사용되는 것처럼 사용자가 제공한 URL은 내부 네트워크에 쓰이는 루프백이나 링크 로컬 주소를 포함한 예약 IP 주소로 연결할 수 없게 됩니다.

이 변경으로 http://127.0.0.1:1021/my-command 같은 URL을 가리키는 테스트 환경의 비공개 통합 기능이 동작하지 않을 수 있습니다.

비공개 통합 기능을 그런 URL로 연결한다면, 로컬 환경에서 Allowed Untrusted Internal Connections 설정으로 해당 도메인·IP 주소·CIDR 표기를 허용 목록에 추가할 수 있습니다. 권장하지는 않지만 운영 환경에서도 해당 주소를 허용 목록에 추가할 수 있습니다. 자세한 내용은 문서를 참고하세요.

푸시 알림, OAuth 2.0, WebRTC 서버 URL은 신뢰되는 것으로 간주되어 이 설정의 영향을 받지 않습니다.

업로드된 첨부 파일은 이제 날짜별로 묶여 파일 저장소의 /data/<date-of-upload-as-YYYYMMDD>/teams/...에 저장됩니다.

Mattermost /platform 저장소가 /mattermost-webapp과 /mattermost-server로 분리되었습니다. /platform 저장소의 비공개 포크를 가지고 있다면 영향을 받을 수 있습니다. 자세한 내용은 여기를 참고하세요.

v4.0.0

(고가용성 환경에 한함)

기존 config.json의 ClusterSettings 섹션에 새 항목을 직접 추가해야 합니다.

v3.9.0

보안 관련 변경으로 기존 이메일 초대 링크, 비밀번호 재설정 링크, 이메일 인증 링크는 더 이상 동작하지 않습니다. 팀 초대 링크 대화 상자에서 복사한 팀 초대 링크는 영향을 받지 않으며 계속 유효합니다.

v3.8.0

프록시 설정을 변경해야 합니다. NGINX를 사용한다면 다음과 같이 하세요.

  1. 루트 권한으로 NGINX 설정 파일을 엽니다. 이 파일은 보통 /etc/nginx/sites-available/mattermost이지만 시스템에 따라 다를 수 있습니다.
  2. location /api/v3/users/websocket { 줄을 찾습니다.
  3. 해당 줄을 location ~ /api/v[0-9]+/(users/)?websocket$ {로 교체합니다.

NGINX가 아닌 다른 프록시를 사용한다면 해당 프록시 설정에 같은 취지의 변경을 적용하세요.

보안 관련 변경으로 시스템 콘솔에서 설정을 확인해야 합니다.

  1. 시스템 콘솔의 GENERAL 섹션으로 이동합니다.
  2. Logging을 클릭합니다.
  3. File Log Directory 필드가 비어 있거나 디렉터리 경로만 담고 있는지 확인합니다. 경로에 파일 이름이 포함되어서는 안 됩니다.

기존 CLI 도구와의 하위 호환성이 제거되었습니다. 기존 CLI에 의존하는 스크립트가 있다면 새 CLI를 사용하도록 수정해야 합니다.

v3.6.0

열 수 있는 파일의 최대 개수를 갱신하세요.

RHEL6 및 Ubuntu 14.04: - /etc/init/mattermost.conf 파일에 limit nofile 50000 50000 줄이 포함되어 있는지 확인하세요. RHEL7 및 Ubuntu 16.04: - /etc/systemd/system/mattermost.service 파일에 LimitNOFILE=49152 줄이 포함되어 있는지 확인하세요.

(Enterprise 전용)

공개 채널과 비공개 채널 관리를 제한하던 기존 config.json 값이 공개·비공개 채널의 생성 및 삭제를 제한하는 새 설정의 기본값으로 사용됩니다.

v3.4.0

공개 링크가 활성화되어 있다면 기존 공개 링크는 더 이상 유효하지 않습니다. 이전 버전에서는 Public Link Salt가 재생성되어도 기존 공개 링크가 무효화되지 않았기 때문입니다. 이 링크를 게시한 모든 위치를 갱신해야 합니다.

추가 업그레이드 참고 사항

참고
  • v12.0(2026년 10월) 릴리스에서 예정된 사용 중단 항목은 사용 중단 기능 문서를 참고하세요.
  • Microsoft Teams Calling 플러그인을 v2.0.0으로 업그레이드하면 사용자가 계정을 다시 연결해야 합니다.
  • Go 1.22.0 및 1.22.1로 빌드한 Mattermost 플러그인은 동작하지 않습니다. 플러그인 개발자는 Go 1.22.2 이상을 사용해야 합니다.
  • Keybase가 Mattermost의 Ubuntu 저장소 서명 키 제공을 중단했습니다. 이 키를 사용하고 있었다면 Linux 배포 문서에 안내된 방식으로 키를 가져오도록 설치 스크립트를 갱신하세요.
  • MySQL 8.0.22는 JSON 컬럼 타입에서 문자열 값을 정수로 바꾸어 Mattermost가 제대로 동작하지 못하게 하는 문제가 있으므로 사용하지 않는 것을 권장합니다.
  • 5.x 릴리스에서 Mattermost 7.x로 업그레이드할 때는 v7.x 업그레이드를 시도하기 전에 먼저 5.37.10으로 업그레이드하세요.
  • 아래에 제시된 스키마 변경 관련 SQL 쿼리는 Mattermost 서버 업그레이드 과정에서 자동으로 실행됩니다. 이 쿼리를 직접 실행할 필요는 없습니다. 이 SQL 쿼리는 업그레이드 시 어떤 데이터베이스 변경이 일어나는지 이해하는 데 도움을 주고, 스키마 변경을 미리 적용해 다운타임을 줄이려는 경우를 위해 제공됩니다.