یک درخواست بازیابی ممکن است سند، نمایهٔ قطعه‌ها و تنظیم بردار را از سه جزء جدا بخواند. اگر میان این خواندن‌ها نسخه عوض شود، حتی سالم‌بودن هر جزء به‌تنهایی برای سازگاری مجموعه کافی نیست. این درس یک manifest کوچک و دو نسخهٔ ساختگی می‌سازد تا اختلاط اجزا را آشکار کند. برنامه در حافظه اجرا می‌شود؛ هیچ موتور جست‌وجو، مدل embedding یا انتشار واقعی نمایه راه‌اندازی نمی‌کند.

۱. تصویر ثابت درخواست را از تازه‌ترین داده جدا کنیم

خواندن یک نسخهٔ ثابت در طول درخواست
تصویر مفهومی: خواندن یک نسخهٔ ثابت در طول درخواست؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

در نسخهٔ R1 دو سند D1 و D2 داریم و قطعه‌های C1 و C2 به ترتیب به آن‌ها متصل‌اند. در R2، سند D1 باقی است، D3 اضافه شده و D2 نشانگر حذف دارد؛ قطعه‌ها C1new و C3 هستند. هر نسخه تنظیم بردارِ نام‌گذاری‌شدهٔ خودش را دارد. پرسش آموزشی این است که یک درخواست کدام مجموعهٔ سازگار را دیده است، نه اینکه کدام پاسخ مرتبط‌تر بوده است.

مستندات point-in-time در Elasticsearch توضیح می‌دهد که تغییر دادهٔ قابل مشاهده میان درخواست‌ها می‌تواند نتیجه‌های ناهماهنگ بسازد و PIT نمایی از وضعیت زمان آغاز فراهم می‌کند. این زمینهٔ مسئله است؛ کلاس حافظه‌ای ما PIT آن محصول را پیاده نمی‌کند. نگهداری نمای قدیمی در سرویس واقعی نیز هزینه و محدودیت دارد و با پشتیبان‌گیری یکی نیست.

یک درخواست، دستهٔ نسخه را یک بار از انتخابگر می‌گیرد. اگر بعداً انتخابگر از R1 به R2 برود، دستهٔ قبلی همچنان R1 است و درخواست تازه R2 می‌گیرد. این ثباتِ تعریف‌شده می‌تواند با الزام نمایش فوری تغییرات تفاوت داشته باشد؛ سیاست کاربرد باید آن را تعیین کند. مثال حاضر فقط این رفتار ترتیبی را قابل مشاهده می‌کند و تضمین هم‌زمانی چند پردازه نمی‌دهد.

۲. manifest چه اجزایی را متعهد می‌کند؟

فهرست صریح اجزای لازم و نشانگر حذف
تصویر مفهومی: فهرست صریح اجزای لازم و نشانگر حذف؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

manifest شامل نام نسخه، فهرست صریح سندهای زنده، شناسهٔ قطعه‌ها، نشانگرهای حذف و چکیدهٔ سه جزء است. سه جزء config، documents و index نام دارند. تنظیم، برچسب ساختگی embedding_tag و بُعد بردار را مشخص می‌کند. جزء سند، متن‌های فرضی و tombstones را دارد. جزء نمایه برای هر قطعه، شناسهٔ سند والد و یک بردار عدد صحیحِ نمونه نگه می‌دارد.

در قرارداد این فایل، یک تا هشت سند و قطعه داریم؛ بُعد از یک تا چهار و مؤلفه‌های بردار اعداد صحیح منفی ده تا ده‌اند. این بردارهای کوچک از مدل زبانی تولید نشده‌اند و شباهت معنایی را نشان نمی‌دهند. همان بُعد نیز همسانی تنظیم را ثابت نمی‌کند؛ دو تنظیم متفاوت می‌توانند هر دو دوبعدی باشند. برای همین، اثر انگشت کل تنظیم با درخواست تطبیق داده می‌شود.

پوشش فهرست‌ها باید دقیق باشد: سند زندهٔ اضافی یا گم‌شده، قطعهٔ اضافی یا گم‌شده و شناسهٔ تکراری رد می‌شوند. هر قطعه باید به سند زنده وصل باشد و هر سند زنده دست‌کم یک قطعه داشته باشد. سند نمی‌تواند هم زنده و هم در tombstones باشد. نشانگر حذف فقط قرارداد همین نسخه است؛ پاک‌شدن فیزیکی متن از همهٔ حافظه‌ها را اثبات نمی‌کند.

۳. هش سالم، نسخهٔ مشترک و پوشش کامل هر سه لازم‌اند

رد اجزای مخلوط با اندازه و نسخهٔ ناسازگار
تصویر مفهومی: رد اجزای مخلوط با اندازه و نسخهٔ ناسازگار؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

چکیدهٔ SHA-256 روی بایت‌های JSON با قالب ثابت همین مثال محاسبه می‌شود. تغییر متن بدون به‌روزرسانی چکیده خطا می‌دهد. اما سازندهٔ داده می‌تواند برای ترکیب اشتباه هم هش تازه بسازد؛ بنابراین پس از کنترل چکیده، برابری نسخه، برچسب تنظیم، طول بردار و رابطهٔ سند و قطعه نیز بررسی می‌شوند. هش در این برنامه امضای ناشر یا اثبات صحت علمی محتوا نیست.

برای جواب مستقل، سه جزء را هر کدام از R1 یا R2 انتخاب می‌کنیم. هشت ترکیب داریم و در هر ترکیب هش‌ها را دوباره می‌سازیم تا خطا صرفاً از چکیدهٔ قدیمی نباشد. پاسخ دستی فقط دو حالت را می‌پذیرد: هر سه جزء از R1 یا هر سه از R2. شش حالت مخلوط رد می‌شوند. این ماتریس هشت‌حالتی یک گروه بررسی است، نه هشت گروه تازه.

در آزمون‌های دیگر، یک ردیف را حذف می‌کنیم ولی فهرست مورد انتظار را نگه می‌داریم، یا قطعه‌ای را به سند ناشناخته وصل می‌کنیم. حتی با هش‌های محاسبه‌شدهٔ تازه، قرارداد پوشش یا رابطه نقض می‌شود. کامل‌بودن نسبت به manifest کنترل می‌شود؛ اگر خود فهرست مرجع اشتباه طراحی شده باشد، این فایل از جهان بیرون سندِ گم‌شده را کشف نمی‌کند.

۴. کد کامل و تفاوت کپی ثابت با نمای زنده

مقایسهٔ تصویر ثابت با نمای زندهٔ قابل تغییر
تصویر مفهومی: مقایسهٔ تصویر ثابت با نمای زندهٔ قابل تغییر؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

فایل index_snapshot_manifest.py با Python 3.14.5 اجرا شده است. پس از موفقیت بررسی‌ها، manifest و اجزا به bytes مستقل تبدیل و در یک dataclass با frozen=True نگه داشته می‌شوند. فیلدها رشته و بایت‌اند؛ فهرست ورودی به داخل شیء وصل نمی‌ماند. تغییر بعدی دیکشنری ورودی، بایت‌های دستهٔ قبلی را عوض نمی‌کند و نتیجهٔ بازرسی نیز کپی تازه است.

"""Synthetic revision-consistency gate, sealed bytes and sequential selection.

No index service, real embedding, atomic filesystem operation or authentication.
"""
from copy import deepcopy
from dataclasses import dataclass, FrozenInstanceError
from itertools import product
from types import MappingProxyType
import hashlib
import json
import platform
import re

PARTS = {'config', 'documents', 'index'}


def identifier(value):
    return type(value) is str and re.fullmatch(r'[A-Za-z][A-Za-z0-9_]{0,15}', value, re.ASCII) is not None


def exact_fields(value, fields, error):
    if type(value) is not dict or set(value) != set(fields):
        raise ValueError(error)


def id_list(value, minimum=0):
    return (type(value) is list and minimum <= len(value) <= 8
            and all(identifier(v) for v in value) and len(set(value)) == len(value))


def encoded(value):
    # Own fixed JSON encoding for these fixtures, not a universal canonicalizer.
    return json.dumps(value, ensure_ascii=False, sort_keys=True,
                      separators=(',', ':'), allow_nan=False).encode('utf-8')


def digest(value):
    return hashlib.sha256(encoded(value)).hexdigest()


@dataclass(frozen=True)
class Snapshot:
    revision: str
    config_sha256: str
    manifest_bytes: bytes
    artifacts_bytes: bytes


def seal(manifest, artifacts):
    exact_fields(manifest, ['revision', 'documents', 'chunks', 'tombstones', 'sha256'], 'manifest_fields')
    exact_fields(artifacts, PARTS, 'artifacts_fields')
    if not identifier(manifest['revision']):
        raise ValueError('revision')
    if not id_list(manifest['documents'], 1) or not id_list(manifest['chunks'], 1) or not id_list(manifest['tombstones']):
        raise ValueError('manifest_ids')
    exact_fields(manifest['sha256'], PARTS, 'digest_fields')
    for part in sorted(PARTS):
        expected = manifest['sha256'][part]
        if type(expected) is not str or re.fullmatch('[0-9a-f]{64}', expected) is None:
            raise ValueError('digest_format')
        if digest(artifacts[part]) != expected:
            raise ValueError('digest_mismatch')
    config, documents, index = (artifacts[p] for p in ('config', 'documents', 'index'))
    exact_fields(config, ['revision', 'embedding_tag', 'dimension'], 'config_fields')
    exact_fields(documents, ['revision', 'records', 'tombstones'], 'document_fields')
    exact_fields(index, ['revision', 'embedding_tag', 'rows'], 'index_fields')
    if any(part['revision'] != manifest['revision'] for part in (config, documents, index)):
        raise ValueError('mixed_revision')
    if (not identifier(config['embedding_tag']) or type(config['dimension']) is not int
            or not 1 <= config['dimension'] <= 4):
        raise ValueError('config')
    if index['embedding_tag'] != config['embedding_tag']:
        raise ValueError('embedding_tag')
    if not id_list(documents['tombstones']) or sorted(documents['tombstones']) != sorted(manifest['tombstones']):
        raise ValueError('tombstones')
    records = documents['records']
    rows = index['rows']
    if type(records) is not list or not 1 <= len(records) <= 8 or type(rows) is not list or not 1 <= len(rows) <= 8:
        raise ValueError('record_count')
    live_ids, chunk_ids, owners = [], [], []
    for record in records:
        exact_fields(record, ['doc_id', 'body'], 'record_fields')
        if (not identifier(record['doc_id']) or type(record['body']) is not str
                or not 1 <= len(record['body']) <= 120
                or any(0xD800 <= ord(c) <= 0xDFFF for c in record['body'])):
            raise ValueError('record')
        live_ids.append(record['doc_id'])
    if len(set(live_ids)) != len(live_ids):
        raise ValueError('duplicate_document')
    if set(live_ids) & set(documents['tombstones']):
        raise ValueError('live_tombstone')
    if sorted(live_ids) != sorted(manifest['documents']):
        raise ValueError('document_coverage')
    for row in rows:
        exact_fields(row, ['chunk_id', 'document_id', 'vector'], 'row_fields')
        if not identifier(row['chunk_id']) or row['document_id'] not in live_ids:
            raise ValueError('document_ref')
        vector = row['vector']
        if type(vector) is not list or len(vector) != config['dimension']:
            raise ValueError('dimension')
        if any(type(v) is not int or not -10 <= v <= 10 for v in vector):
            raise ValueError('vector')
        chunk_ids.append(row['chunk_id'])
        owners.append(row['document_id'])
    if len(set(chunk_ids)) != len(chunk_ids):
        raise ValueError('duplicate_chunk')
    if sorted(chunk_ids) != sorted(manifest['chunks']):
        raise ValueError('chunk_coverage')
    if set(owners) != set(live_ids):
        raise ValueError('document_without_chunks')
    return Snapshot(manifest['revision'], manifest['sha256']['config'], encoded(manifest), encoded(artifacts))


def inspect_query(snapshot, query_config_sha256):
    # Only handles produced by seal; the Python type is not a trust boundary.
    if query_config_sha256 != snapshot.config_sha256:
        raise ValueError('query_config')
    artifacts = json.loads(snapshot.artifacts_bytes)
    return {'revision': snapshot.revision,
            'eligible_chunk_document_pairs': sorted([[r['chunk_id'], r['document_id']]
                                                      for r in artifacts['index']['rows']]),
            'tombstones': sorted(artifacts['documents']['tombstones'])}


class SequentialSelector:
    def __init__(self):
        self._catalog = {}
        self._active = None

    def install(self, manifest, artifacts):
        candidate = seal(manifest, artifacts)
        if candidate.revision in self._catalog:
            raise ValueError('revision_exists')
        self._catalog[candidate.revision] = candidate

    def select(self, revision):
        if type(revision) is not str or revision not in self._catalog:
            raise ValueError('unknown_revision')
        self._active = revision

    def acquire(self):
        if self._active is None:
            raise ValueError('no_active_revision')
        return self._catalog[self._active]


def fixture(revision='R1'):
    if revision == 'R1':
        ids, chunks, dead, tag = ['D1', 'D2'], ['C1', 'C2'], [], 'FixtureA'
    else:
        ids, chunks, dead, tag = ['D1', 'D3'], ['C1new', 'C3'], ['D2'], 'FixtureB'
    artifacts = {
      'config': {'revision': revision, 'embedding_tag': tag, 'dimension': 2},
      'documents': {'revision': revision, 'records': [{'doc_id': i, 'body': 'سند ساختگی ' + i} for i in ids], 'tombstones': dead},
      'index': {'revision': revision, 'embedding_tag': tag,
                'rows': [{'chunk_id': c, 'document_id': d, 'vector': v}
                         for c, d, v in zip(chunks, ids, [[1, 0], [0, 1]])]}}
    manifest = {'revision': revision, 'documents': ids, 'chunks': chunks,
                'tombstones': list(dead), 'sha256': {p: digest(artifacts[p]) for p in PARTS}}
    return manifest, artifacts


def refreshed_hashes(manifest, artifacts):
    result = deepcopy(manifest)
    result['sha256'] = {p: digest(artifacts[p]) for p in PARTS}
    return result


def rejected(call, message):
    try:
        call()
    except ValueError as error:
        return str(error) == message
    return False


def mutated_rejection(change, error, manifest_change=None):
    manifest, artifacts = fixture()
    change(artifacts)
    if manifest_change:
        manifest_change(manifest)
    return rejected(lambda: seal(refreshed_hashes(manifest, artifacts), artifacts), error)


def run_example():
    m1, a1 = fixture()
    m2, a2 = fixture('R2')
    before = deepcopy((m1, a1))
    s1 = seal(m1, a1)
    hand1 = {'revision': 'R1', 'eligible_chunk_document_pairs': [['C1', 'D1'], ['C2', 'D2']], 'tombstones': []}
    hand2 = {'revision': 'R2', 'eligible_chunk_document_pairs': [['C1new', 'D1'], ['C3', 'D3']], 'tombstones': ['D2']}
    mixed = []
    for bits in product([0, 1], repeat=3):
        artifacts = {p: deepcopy((a1, a2)[bit][p]) for p, bit in zip(['config', 'documents', 'index'], bits)}
        manifest = deepcopy((m1, m2)[bits[0]])
        manifest = refreshed_hashes(manifest, artifacts)
        try:
            seal(manifest, artifacts)
            mixed.append(True)
        except ValueError:
            mixed.append(False)
    tampered = deepcopy(a1)
    tampered['documents']['records'][0]['body'] = 'متن عوض‌شده'
    selector = SequentialSelector()
    no_active = rejected(selector.acquire, 'no_active_revision')
    selector.install(m1, a1)
    selector.select('R1')
    acquired_old = selector.acquire()
    bad_install = rejected(lambda: selector.install(m1, tampered), 'digest_mismatch')
    active_survives = selector.acquire() == acquired_old
    selector.install(m2, a2)
    selector.select('R2')
    acquired_new = selector.acquire()
    unknown_rejected = rejected(lambda: selector.select('R3'), 'unknown_revision')
    unknown_unchanged = selector.acquire() == acquired_new
    replacement_rejected = rejected(lambda: selector.install(m2, a2), 'revision_exists')
    raw_before = (s1.manifest_bytes, s1.artifacts_bytes)
    m1['documents'].append('D8')
    a1['index']['rows'][0]['vector'][0] = 9
    isolated = raw_before == (s1.manifest_bytes, s1.artifacts_bytes) and inspect_query(s1, s1.config_sha256) == hand1
    m1, a1 = deepcopy(before)
    frozen = False
    try:
        s1.revision = 'R9'
    except FrozenInstanceError:
        frozen = True
    backing = {'revision': 'R1'}
    view = MappingProxyType(backing)
    backing['revision'] = 'R2'
    returned = inspect_query(s1, s1.config_sha256)
    returned['eligible_chunk_document_pairs'][0][0] = 'changed'
    wrong_config = digest(dict(a1['config'], embedding_tag='Other'))
    invalid_manifest = deepcopy(m1)
    invalid_manifest['sha256']['index'] = 'bad'
    extra_manifest = dict(m1, extra=True)
    missing_artifact = {k: v for k, v in a1.items() if k != 'index'}
    checks = {
      'hand_two_revision_manifest_outputs': inspect_query(s1, s1.config_sha256) == hand1 and inspect_query(acquired_new, acquired_new.config_sha256) == hand2,
      'independent_eight_mix_hand_matrix': mixed == [True, False, False, False, False, False, False, True],
      'payload_change_without_digest_refresh_rejects': rejected(lambda: seal(m1, tampered), 'digest_mismatch'),
      'embedding_tag_mismatch_rejects_even_with_new_hash': mutated_rejection(lambda a: a['index'].update(embedding_tag='Other'), 'embedding_tag'),
      'vector_dimension_mismatch_rejects_even_with_new_hash': mutated_rejection(lambda a: a['index']['rows'][0].update(vector=[1]), 'dimension'),
      'declared_live_document_and_chunk_completeness': mutated_rejection(lambda a: a['documents']['records'].pop(), 'document_coverage') and mutated_rejection(lambda a: a['index']['rows'].pop(), 'chunk_coverage'),
      'unknown_chunk_owner_rejects': mutated_rejection(lambda a: a['index']['rows'][0].update(document_id='D9'), 'document_ref'),
      'tombstone_live_conflict_and_manifest_mismatch_reject': mutated_rejection(lambda a: a['documents']['tombstones'].append('D2'), 'live_tombstone', lambda m: m['tombstones'].append('D2')) and mutated_rejection(lambda a: a['documents']['tombstones'].append('D8'), 'tombstones'),
      'every_declared_live_document_has_a_chunk': mutated_rejection(lambda a: a['index']['rows'].pop(), 'document_without_chunks', lambda m: m['chunks'].pop()),
      'duplicate_document_and_chunk_ids_reject': mutated_rejection(lambda a: a['documents']['records'].append(deepcopy(a['documents']['records'][0])), 'duplicate_document') and mutated_rejection(lambda a: a['index']['rows'].append(deepcopy(a['index']['rows'][0])), 'duplicate_chunk'),
      'schema_digest_and_required_artifact_contracts': rejected(lambda: seal(invalid_manifest, a1), 'digest_format') and rejected(lambda: seal(extra_manifest, a1), 'manifest_fields') and rejected(lambda: seal(m1, missing_artifact), 'artifacts_fields'),
      'dimension_and_integer_fixture_vector_bounds': mutated_rejection(lambda a: a['config'].update(dimension=True), 'config') and all(mutated_rejection(lambda a, value=value: a['index']['rows'][0].update(vector=[value, 0]), 'vector') for value in [True, 1.0, 11, -11]),
      'failed_candidate_does_not_change_active_revision': no_active and bad_install and active_survives,
      'acquired_revision_survives_later_selection': inspect_query(acquired_old, acquired_old.config_sha256) == hand1 and inspect_query(acquired_new, acquired_new.config_sha256) == hand2,
      'sealed_bytes_and_frozen_fields_ignore_input_mutation': isolated and frozen and type(s1.artifacts_bytes) is bytes,
      'query_configuration_fingerprint_must_match': rejected(lambda: inspect_query(s1, wrong_config), 'query_config'),
      'unknown_selection_and_revision_replacement_reject': unknown_rejected and unknown_unchanged and replacement_rejected,
      'dynamic_proxy_is_not_snapshot_and_results_are_fresh_copies': view['revision'] == 'R2' and inspect_query(s1, s1.config_sha256) == hand1,
    }
    assert len(checks) == 18 and all(type(value) is bool and value for value in checks.values()), checks
    return {'python': platform.python_version(), 'dependencies': 'Python standard library only',
            'scope': 'Synthetic bounded revision-consistency validation and sequential in-memory handles; no actual embeddings, search engine, authorization or atomic deployment',
            'results': {'old_acquired_request': hand1, 'new_acquired_request': hand2,
                        'mixed_artifact_hand_matrix_order_config_documents_index': mixed,
                        'valid_mix_count_inside_one_check': sum(mixed),
                        'sealed_artifact_bytes_R1': len(s1.artifacts_bytes),
                        'configuration_sha256_R1': s1.config_sha256,
                        'proxy_after_backing_change': view['revision']}, 'checks': checks}


if __name__ == '__main__':
    print(json.dumps(run_example(), ensure_ascii=False, indent=2))

MappingProxyType در مستندات پایتون نمای فقط‌خواندنیِ زنده است؛ تغییر دیکشنری زیرین در آن دیده می‌شود. آزمون این تفاوت را نشان می‌دهد. frozen در dataclass نیز تغییرناپذیری را شبیه‌سازی می‌کند و انتساب معمولی را رد می‌کند؛ این ویژگی مرز امنیتی در برابر کدِ داخل همان فرایند نیست. دسته‌های این تمرین باید از تابع seal ساخته شده باشند.

{
  "python": "3.14.5",
  "dependencies": "Python standard library only",
  "scope": "Synthetic bounded revision-consistency validation and sequential in-memory handles; no actual embeddings, search engine, authorization or atomic deployment",
  "results": {
    "old_acquired_request": {
      "revision": "R1",
      "eligible_chunk_document_pairs": [
        [
          "C1",
          "D1"
        ],
        [
          "C2",
          "D2"
        ]
      ],
      "tombstones": []
    },
    "new_acquired_request": {
      "revision": "R2",
      "eligible_chunk_document_pairs": [
        [
          "C1new",
          "D1"
        ],
        [
          "C3",
          "D3"
        ]
      ],
      "tombstones": [
        "D2"
      ]
    },
    "mixed_artifact_hand_matrix_order_config_documents_index": [
      true,
      false,
      false,
      false,
      false,
      false,
      false,
      true
    ],
    "valid_mix_count_inside_one_check": 2,
    "sealed_artifact_bytes_R1": 389,
    "configuration_sha256_R1": "d6e460dddc0440e89973051d1875be2faeade2dfc73f5885795d95c193cabd5b",
    "proxy_after_backing_change": "R2"
  },
  "checks": {
    "hand_two_revision_manifest_outputs": true,
    "independent_eight_mix_hand_matrix": true,
    "payload_change_without_digest_refresh_rejects": true,
    "embedding_tag_mismatch_rejects_even_with_new_hash": true,
    "vector_dimension_mismatch_rejects_even_with_new_hash": true,
    "declared_live_document_and_chunk_completeness": true,
    "unknown_chunk_owner_rejects": true,
    "tombstone_live_conflict_and_manifest_mismatch_reject": true,
    "every_declared_live_document_has_a_chunk": true,
    "duplicate_document_and_chunk_ids_reject": true,
    "schema_digest_and_required_artifact_contracts": true,
    "dimension_and_integer_fixture_vector_bounds": true,
    "failed_candidate_does_not_change_active_revision": true,
    "acquired_revision_survives_later_selection": true,
    "sealed_bytes_and_frozen_fields_ignore_input_mutation": true,
    "query_configuration_fingerprint_must_match": true,
    "unknown_selection_and_revision_replacement_reject": true,
    "dynamic_proxy_is_not_snapshot_and_results_are_fresh_copies": true
  }
}

هجده گروه اجرایی، ماتریس دستی، اختلاط نسخه، پیکربندی ناسازگار، ابعاد نادرست، حذف ناقص، شناسهٔ تکراری و اثر تغییر ورودی را می‌سنجند. نصب ناموفق، نسخهٔ فعال را عوض نمی‌کند؛ جایگزینی بی‌اعلان یک نام نسخه هم رد می‌شود. درخواستِ دارای اثر انگشت تنظیم دیگر، حتی با بُعد برابر، خطا می‌گیرد. برنامه فقط زوج قطعه و سند را گزارش می‌کند و رتبه‌بندی یا embedding اجرا نمی‌کند.

۵. مرز عملی و چهار پرسش پاسخ‌دار

آزمون انتخاب نسخه بدون ادعای استقرار واقعی
تصویر مفهومی: آزمون انتخاب نسخه بدون ادعای استقرار واقعی؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

انتخابگر، نام نسخهٔ فعال را در حافظه تغییر می‌دهد. از این انتساب ترتیبی، اتمی‌بودن فایل، تراکنش توزیع‌شده، بازیابی خرابی یا دوام دیسک نتیجه نمی‌گیریم. به‌روزرسانی عملی نمایه و ساخت نمایهٔ معکوس موضوعات مرتبط‌اند؛ آزمون سازگاری این درس جای طراحی آن مسیرها را نمی‌گیرد.

پرسش اول: چرا R1 سند D2 را دارد ولی R2 ندارد؟ پاسخ: R2 عمداً D2 را در tombstones قرار داده و قطعهٔ آن را ندارد. دستهٔ قدیمی R1 همچنان همان نمای قدیمی را می‌بیند؛ این رفتار برای لغو فوری دسترسی کافی نیست و سیاست آن جداست.

پرسش دوم: آیا هش تازه، ترکیب سند R1 و نمایهٔ R2 را مجاز می‌کند؟ پاسخ: خیر؛ پس از کنترل بایت‌ها، یکسان‌بودن نسخهٔ اجزا الزام است. محاسبهٔ دوبارهٔ هش، رابطهٔ نسخه‌های ناسازگار را اصلاح نمی‌کند.

پرسش سوم: آیا دو بردار دوبعدی همیشه قابل ترکیب‌اند؟ پاسخ: خیر؛ تنظیم تولید آن‌ها می‌تواند متفاوت باشد. این مثال برچسب و اثر انگشت تنظیم را بررسی می‌کند، ولی اثبات نمی‌کند بردار واقعاً از متن و مدل اعلام‌شده تولید شده است.

پرسش چهارم: اگر نسخهٔ تازه بررسی را نگذرد، درخواست چه می‌بیند؟ پاسخ: نصب خطا می‌دهد و انتخاب فعال قبلی در آزمون باقی می‌ماند؛ انتخاب خودکار یا بازنویسی نسخه نداریم. منابع در ۲۴ مهر ۱۴۰۵ خوانده شدند. شمارهٔ 7.10.0 در صفحهٔ PIT زمان افزوده‌شدن قابلیت است؛ این آموزش پایه، خبر تازه یا آزمون Elasticsearch نیست.

منابع و مطالعهٔ بیشتر

بررسی منابع: ۲۴ مهر ۱۴۰۵. تحلیل‌ها و پیشنهادهای اجرایی، جمع‌بندی نویسنده‌اند.