DRAFT: deferred index creation - no table version - do not merge#373
Open
michal-morzywolek wants to merge 68 commits intomainfrom
Open
DRAFT: deferred index creation - no table version - do not merge#373michal-morzywolek wants to merge 68 commits intomainfrom
michal-morzywolek wants to merge 68 commits intomainfrom
Conversation
Implements 14 test methods covering: - Valid steps with @Version and package-based versioning - Sequence ordering and validation - Error detection for missing/duplicate sequences - Invalid version format detection - Package name validation - Multiple validation error accumulation Increases coverage from 56% to 94% instruction coverage. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DeferredIndexStatus enum: PENDING, IN_PROGRESS, COMPLETED, FAILED - DeferredIndexOperationType enum: ADD - DeferredIndexOperation domain class representing a row from the DeferredIndexOperation table plus ordered column names - DeferredIndexOperationDAO for all CRUD operations on the deferred index queue, following the UpgradeStatusTableServiceImpl pattern (SqlScriptExecutorProvider + SqlDialect, enum values stored via .name()) - 10 unit tests using ArgumentCaptor to verify DSL statement structure Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ce split - Add DeferredAddIndex: SchemaChange for deferred index creation; apply() updates metadata only, reverse() removes index from metadata, isApplied() checks actual DB schema first then DeferredIndexOperation PENDING queue via DAO - Split DeferredIndexOperationDAO into interface (@ImplementedBy) + DAOImpl class, following UpgradeStatusTableService/Impl convention - Wire DeferredAddIndex into SchemaChangeVisitor (visit method), AbstractSchemaChangeVisitor (updates schema, no DDL), SchemaChangeSequence.InternalVisitor, and SchemaChangeAdaptor (default + Combining) - Add 11 tests for DeferredAddIndex covering apply, reverse, isApplied, and accept - Rename TestDeferredIndexOperationDAO -> TestDeferredIndexOperationDAOImpl Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add addIndexDeferred() to SchemaEditor interface and SchemaChangeSequence.Editor: creates a DeferredAddIndex carrying the step's @uuid, calls visitor.visit() only (no schemaAndDataChangeVisitor — no DDL runs on the target table during upgrade) - AbstractSchemaChangeVisitor.visit(DeferredAddIndex): write INSERT SQL into DeferredIndexOperation and DeferredIndexOperationColumn as part of the upgrade script; upgradeUUID read from DeferredAddIndex rather than stored visitor state - DeferredAddIndex: add upgradeUUID field + getter; updated toString() to include it - HumanReadableStatementProducer: implement addIndexDeferred() via generateAddIndexString - Tests: TestInlineTableUpgrader, TestSchemaChangeSequence, TestDeferredAddIndex updated Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces DeferredIndexChangeService (interface + DeferredIndexChangeServiceImpl) to track pending deferred ADD INDEX operations within an upgrade session and emit the compensating SQL when subsequent schema changes interact with them: - RemoveIndex on a pending deferred ADD → cancel (DELETE) instead of DROP INDEX - RemoveTable → cancel all pending deferred indexes on that table - RemoveColumn → cancel pending deferred indexes referencing that column - RenameTable → UPDATE tableName in PENDING rows and update in-memory tracking - ChangeColumn (rename) → UPDATE columnName in PENDING column rows AbstractSchemaChangeVisitor delegates entirely to DeferredIndexChangeService, keeping SQL construction out of the visitor. The service is independently tested with 19 unit tests covering edge cases: multiple indexes on the same table, partial column matches, case-insensitivity, and single-UPDATE coverage for multi-index column renames. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Stage 7: DeferredIndexExecutor picks up PENDING operations, builds indexes via SqlDialect.deferredIndexDeploymentStatements(), and manages retry with exponential backoff. Progress logged at 30s intervals. Stage 8: awaitCompletion() polls the queue for multi-instance deployments where non-executor nodes must block until index builds finish. Stage 9: DeferredIndexRecoveryService detects stale IN_PROGRESS operations (exceeded staleThresholdSeconds) and resets or completes them based on whether the index exists in the schema. Stage 10: DeferredIndexValidator force-executes any PENDING operations before a new upgrade runs, ensuring no missing indexes. Supporting changes: DeferredIndexTimestamps utility, retryBaseDelayMs config field, DAO.hasNonTerminalOperations(), SqlDialect base method for deferred index DDL. 28 tests (10 executor, 6 recovery, 4 validator, 8 unit) all passing. Coverage: Executor 87%/81%, Recovery 100%, Validator 100%, Timestamps 100%. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Override deferredIndexDeploymentStatements() in PostgreSQLDialect (CREATE INDEX CONCURRENTLY) and OracleDialect (ONLINE PARALLEL NOLOGGING) so deferred index builds avoid table-level write locks. DeferredIndexExecutor.buildIndex() now uses a dedicated autocommit connection because PostgreSQL's CONCURRENTLY cannot run inside a transaction block. This is harmless for other platforms. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 H2 integration tests covering: pending row creation, executor completion, auto-cancel, unique/multi-column/new-table indexes, populated table builds, multiple indexes per step, executor idempotency, and recovery-to-execution pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…x/RenameIndex deferred handling - Fix DeferredIndexChangeServiceImpl to use DeferredIndexTimestamps.currentTimestamp() instead of System.currentTimeMillis() for createdTime (yyyyMMddHHmmss format) - Fix DeferredIndexOperationDAOImpl to use literal(boolean)/getBoolean() for the indexUnique column instead of literal(int)/getInt() - Add ChangeIndex/RenameIndex handling for pending deferred indexes in AbstractSchemaChangeVisitor: ChangeIndex cancels the deferred op and adds the new index immediately; RenameIndex updates the queued index name - Add updatePendingIndexName() to DeferredIndexChangeService/Impl - Add unit tests for deferred branches in both visitor test classes - Add integration tests for deferred-add-then-change and deferred-add-then-rename Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add retryMaxDelayMs config (default 5 min) to DeferredIndexConfig to cap exponential backoff and prevent overflow at high retry counts - DeferredIndexValidator now throws IllegalStateException when forced execution fails, blocking the upgrade until the issue is resolved - Update integration test to expect the exception on failed forced execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ry tracking - Add retryMaxDelayMs config (default 5 min) to cap exponential backoff and prevent overflow at high retry counts (#4) - DeferredIndexValidator throws IllegalStateException when forced execution fails, blocking the upgrade until resolved (#5) - updatePendingColumnName/updatePendingTableName now rebuild in-memory DeferredAddIndex entries so cancelPendingReferencingColumn finds indexes by renamed column/table names (#7) - Add unit and integration tests for all three fixes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace STRING(100) operationId PK on DeferredIndexOperation with BIG_INTEGER id column. Change DeferredIndexOperationColumn FK from STRING(100) to BIG_INTEGER to match. Update domain class, DAO interface/impl, services, executor, recovery service, and all tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
findOperationsByStatus and findStaleInProgressOperations now use a single LEFT OUTER JOIN query to fetch operations with their column names, eliminating per-operation column lookups. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DeferredAddIndex.apply() now uses equalsIgnoreCase() for index name comparison, consistent with reverse() and other SchemaChange classes. DeferredIndexChangeServiceImpl SQL statements now use the original casing from stored DeferredAddIndex entries rather than the caller's casing, ensuring SQL WHERE clauses match rows on case-sensitive databases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
tableName, indexName, and columnName were STRING(30) which is too narrow for the validated maximum identifier length of 60 characters. Made SchemaValidator.MAX_LENGTH public and referenced it directly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove existsByUpgradeUUIDAndIndexName from DAO interface and implementation — no production code calls it. Update stale comment in SchemaChangeSequence that referenced a future stage. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cted directly The DAO is never injected via Guice — all consumers construct it directly with ConnectionResources. Remove the misleading annotations to match the actual usage pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
addIndexDeferred now prefixes "Deferred: " so the output is distinguishable from a regular addIndex operation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each service now validates the config fields it consumes at construction time: threadPoolSize, maxRetries, retry delays, staleThresholdSeconds, and operationTimeoutSeconds. Also fixes stale comment in SchemaChangeSequence and distinguishes deferred index in human-readable output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…vate Introduce DeferredIndexService as the single public entry point for adopters. The facade orchestrates recovery, execution, and failure detection in one execute() call, and provides awaitCompletion() for passive nodes. Internal classes (Executor, RecoveryService, Validator, Operation, OperationType, Status) are now package-private. Config validation is consolidated in DeferredIndexServiceImpl. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update column and index name assertions to match actual table structure after previous refactoring (operationId → id, updated index names). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- TestDeferredIndexValidatorUnit (5 tests): validates empty queue shortcut, successful execution, failure exception with count - TestDeferredIndexRecoveryServiceUnit (6 tests): stale recovery for index-exists, index-absent, table-missing, multiple ops, and case-insensitive index name matching - TestDeferredIndexExecutorUnit additions (8 tests): empty queue, single success, retry-then-success, permanent failure, getStatus before/after execution, awaitCompletion true/false paths - Added test constructors to DeferredIndexValidator and DeferredIndexRecoveryService for mock injection - Made DeferredIndexValidator.createExecutor() overridable Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- TestDeferredIndexOperation (2 tests): full POJO getter/setter coverage including nullable fields → 100% line coverage - TestDeferredAddIndex additions (4 tests): toString(), apply/reverse with existing other indexes, isApplied with non-matching index → 100% line coverage - TestDeferredIndexExecutorUnit additions (3 tests): unique index reconstruction, SQLException from getConnection, zero-timeout awaitCompletion → 83% line coverage Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace internal factory methods with proper Guice @Inject/@singleton wiring so that DeferredIndexService can be injected by adopters. All services now share a single DAO instance instead of each creating its own. Bind DeferredIndexConfig in MorfModule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Deployers can now configure a set of index names that should be built immediately during upgrade even when the upgrade step uses addIndexDeferred(). This allows overriding deferral for critical indexes without modifying upgrade steps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Deployers can now configure a set of index names that should be deferred even when the upgrade step uses addIndex(). This enables retroactive deferred index creation on old upgrade steps without modifying them. Includes conflict validation that throws if an index name appears in both forceImmediateIndexes and forceDeferredIndexes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…to upgrade Rename the pre-upgrade readiness check to better reflect its purpose. Wire it into Upgrade.findPath() so it runs automatically for both the sequential and graph-based upgrade paths. If the DeferredIndexOperation table does not yet exist (first upgrade), the check is a safe no-op. Post-upgrade deferred index execution is NOT auto-wired — adopters must explicitly call DeferredIndexService.execute() after upgrade. The readiness check serves as a safety net: any forgotten pending operations are force-built before the next upgrade proceeds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ation In managed environments (e.g. servlet containers), unmanaged daemon threads bypass container lifecycle and classloader management. This extracts thread pool creation into a Guice-overridable factory so adopters can provide a container-managed ExecutorService (e.g. wrapping commonj WorkManager). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Inject SqlScriptExecutorProvider instead of creating it internally, allowing unit tests to mock it through the single Guice constructor rather than needing a separate test-only constructor. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove dedicated progress logger thread (caused deadlock with pool size 1, leaked unmanaged threads in servlet containers) - Log progress after each operation completes instead of on a timer - Add DAO.countAllByStatus() for single-query progress reporting - Remove AtomicInteger counters (query DB for live counts instead)
Exposes DAO.countAllByStatus() through the public facade so adopters can poll progress from their own timer, health endpoint, or JMX bean.
…store, remove truncate - Promote debug logging in executeWithRetry to INFO (start/complete) and ERROR (failure) - Log elapsed time in seconds for each attempt (success and failure) - Log final progress and completion message in whenComplete callback - Save/restore autocommit on connection (consistent with rest of codebase) - Remove truncate() — errorMessage column is CLOB, no length limit
…, inject SqlScriptExecutorProvider - Remove countByStatus() and countFailedOperations() from DAO; callers use countAllByStatus() - Reject executionTimeoutSeconds <= 0 consistently (no "wait forever") - Throw IllegalStateException on ExecutionException in awaitCompletion() - Inject SqlScriptExecutorProvider into DeferredIndexOperationDAOImpl via @Inject constructor - Delete DeferredIndexOperationType enum and operationType column (premature abstraction, only value was ADD) - Update all tests and integration tests for constructor and API changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
shutdownNow() cannot stop a running CREATE INDEX (database-side operation). The whenComplete callback in execute() already calls threadPool.shutdown() — that is the correct cleanup path. Remove the shutdown() method from the interface, its implementation, the three executor.shutdown() calls in DeferredIndexReadinessCheckImpl catch blocks, and two unit tests that exercised it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e failures) Tests cover Mode 1 (force-build), Mode 2 (background), crash recovery, and multi-upgrade scenarios. Will compile once Stage C-F are implemented. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…DeferredIndexBuildOnRestart - Rename config class to better reflect its executor-runtime scope - Remove staleThresholdSeconds (no longer needed; recovery service will be removed) - Add forceDeferredIndexBuildOnRestart to UpgradeConfigAndContext (Mode 1/2 toggle) - Update all references across codebase - Remove config parameter from DeferredIndexRecoveryServiceImpl constructor Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ema augmentation for Mode 2 - DeferredIndexReadinessCheck.run() no longer takes Schema param; checks table existence internally via ConnectionResources - run() resets IN_PROGRESS → PENDING before querying (crash recovery) - Add augmentSchemaWithDeferredIndexes() for Mode 2 schema augmentation - Add noOp() factory for test contexts - Upgrade.findPath(): Mode 1 runs readiness check before sourceSchema read; Mode 2 augments sourceSchema after read - DAO: add resetAllInProgressToPending() and findNonTerminalOperations() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…erviceImpl - Executor resets IN_PROGRESS → PENDING at start of execute() - Post-failure index-exists check: if CREATE INDEX fails but index exists in DB, mark COMPLETED (handles previous crashed build) - Store connectionResources in executor for schema inspection - Remove DeferredIndexRecoveryService dependency from DeferredIndexServiceImpl - Update all tests for new constructor signatures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lidation - Delete DeferredIndexRecoveryService and its impl/tests (crash recovery now handled by executor's IN_PROGRESS→PENDING reset + post-failure check) - Remove findStaleInProgressOperations from DAO interface and impl - Rename integration test to reflect executor-only recovery flow - Fix AddSecondDeferredIndex to use composite index (id,name) instead of PK-only index which fails schema validation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…dd augment tests - Add toIndex() to DeferredIndexOperation, remove duplicate reconstructIndex from DeferredIndexExecutorImpl and DeferredIndexReadinessCheckImpl - Remove dead updateStatus and hasNonTerminalOperations from DAO interface, impl, and test (zero production callers) - Add 7 unit tests for augmentSchemaWithDeferredIndexes edge cases: table missing, no ops, index already exists, unique index, multiple tables - Add missing COMPLETED assertion to Mode 2 lifecycle test - Clarify Javadoc on timeout semantics: executionTimeoutSeconds (must be >0, pre-upgrade safety) vs awaitCompletion(0) (wait forever, post-startup opt-in) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…n method - DeferredIndexExecutorImpl: remove cached sqlDialect/dataSource fields, call through connectionResources directly - DeferredIndexOperationDAO: remove insertOperation() which had zero production callers (inserts are done via DeferredIndexChangeServiceImpl) - Clean up unused imports (SqlDialect, DataSource, UUID, insert, ResultSetProcessor, anyList) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- DAO resetAllInProgressToPending: replace SELECT+conditional UPDATE with a simple UPDATE WHERE, change return type to void - Remove DeferredIndexReadinessCheck.noOp() test helper from production code; tests now use mock() instead - Fix javadoc: "pre-upgrade" -> "startup", "new upgrade" -> "previous run" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rename run() -> forceBuildAllPending() for clarity - Rename augmentSchemaWithDeferredIndexes() -> augmentSchemaWithPendingIndexes() - Extract awaitCompletion() to separate timeout/interrupt handling - Add log + comment explaining stale row cleanup when index already exists Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…move guard - Inline table definitions in CreateDeferredIndexOperationTables to decouple from DatabaseUpgradeTableContribution (matches pattern of other upgrade steps) - ChangeIndex on a pending deferred index now cancels and re-defers the replacement instead of creating it immediately - Add getPendingDeferred returning Optional to DeferredIndexChangeService - Remove unnecessary name-equality guard in visit(ChangeColumn) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Single-threaded startup sequence, no cross-thread visibility needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… tests - Throw IllegalStateException on double execute() in DeferredIndexExecutorImpl - Add try/catch for unknown status values in countAllByStatus() - Add thread naming counter in DeferredIndexExecutorServiceFactory - Remove unused SKIPPED status from DeferredIndexStatus enum - Add config validation in readiness check forceBuildAllPending() - Cap backoff shift to prevent overflow in sleepForBackoff() - Rename DeferredIndexOp_3 to DeferredIndexOp_2 (fill numbering gap) - Add comment explaining dual resetAllInProgressToPending calls - Replace Thread.sleep with CountDownLatch in service tests - Wrap force-immediate/deferred test config in try/finally - Add DAO unit tests for resetAll, countAllByStatus, findNonTerminal - Close MockitoAnnotations.openMocks in @after methods - Replace null executor with mock in readiness check tests - Simplify column-name assertion in integration test - Delete unused buildOperation helper in DAO test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Always augment source schema with pending deferred indexes - Force-build only when upgrade steps exist (not on every restart) - Remove forceDeferredIndexBuildOnRestart flag from UpgradeConfigAndContext - Fix executor reuse bug: null out threadPool after shutdown so Guice singleton can be called again after forceBuildAllPending - Fix forceBuildAllPending: check FAILED ops even when no PENDING ops exist, preventing upgrades with stale failed indexes - Add per-index INFO log during schema augmentation - Rewrite lifecycle tests for unified behavior - Add test for executor reuse after completion - Add test for FAILED ops blocking force-build before upgrade Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ield tests - Replace bare @OverRide methods with @see Javadoc pattern matching existing Morf codebase convention across all files on this branch - Split TestDeferredIndexOperation.testAllGettersAndSetters into 12 individual test methods with Javadoc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nsupported platforms - Add SqlDialect.supportsDeferredIndexCreation() returning false by default - Override to true in PostgreSQLDialect, OracleDialect, H2Dialect - MySQL/SQL Server inherit false — addIndexDeferred() silently becomes addIndex() - Check in AbstractSchemaChangeVisitor.visit(DeferredAddIndex) at execution time - Update HumanReadableStatementProducer to dialect-neutral log message - Add dialect test in AbstractSqlDialectTest with per-dialect overrides - Add fallback unit test in TestInlineTableUpgrader - Add integration test verifying unsupported dialect builds index immediately Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove DeferredIndexOperation/DeferredIndexOperationColumn tracking tables and replace with a replay-based approach: replay all upgrade steps to find DeferredAddIndex changes, resolve cascades via DeferredIndexTracker, compare surviving deferred indexes against the live DB schema, and build missing ones. New classes: - DeferredIndexTracker: in-memory cascade resolution (void methods) - DeferredIndexProgress: progress POJO replacing Map<DeferredIndexStatus, Integer> - SchemaChangeSequence.findSurvivingDeferredIndexes(): replay discovery Deleted classes (7 production, 3 test): - DeferredIndexOperation, DeferredIndexOperationDAO/Impl, DeferredIndexStatus - DeferredIndexChangeService/Impl, CreateDeferredIndexOperationTables Key changes: - DeferredIndexExecutor.execute() takes List<DeferredAddIndex> instead of no-arg - DeferredIndexReadinessCheck methods take Collection<Class<? extends UpgradeStep>> - DeferredIndexService requires setUpgradeSteps() before execute() - AbstractSchemaChangeVisitor uses DeferredIndexTracker (no SQL emitted for deferred ops) - DeferredAddIndex.isApplied() only checks DB schema (no queue check) - Upgrade.findPath() filters to applied steps for augmentation/force-build - DatabaseUpgradeTableContribution no longer defines tracking tables - H2v2 dialect now overrides supportsDeferredIndexCreation() to return true Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



No description provided.