Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfixes storage postgres interface and added repo integration tests #285

Merged
merged 70 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from 65 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
b8e46b8
Moved all filter logic in GetInstancesFromQuery into the database
HenningNormann Jul 15, 2023
d29bbdb
- Added index instances_partyid_lastchanged
HenningNormann Aug 3, 2023
f8ca1cd
Fix for excludeConfirmedBy
HenningNormann Aug 7, 2023
6523b1a
- Forced default sort order in GetInstances
HenningNormann Aug 10, 2023
931d6cd
Added more valid properties to update in data element
HenningNormann Aug 10, 2023
801bdab
Merge from main
HenningNormann Aug 23, 2023
fdd1ce4
Ensured that internalId is always sent to dataelement create.
HenningNormann Aug 25, 2023
36c7b77
Avoid ambigious reference to event
HenningNormann Aug 28, 2023
d4c9622
Avoid ambigious reference to event - take 2
HenningNormann Aug 28, 2023
707537a
Avoid ambigious reference to event - take 3
HenningNormann Aug 28, 2023
cd331c6
Changed delete procedures to functions to be able to return row count.
HenningNormann Aug 28, 2023
b496c28
Fixed ListInstanceEvents
HenningNormann Aug 29, 2023
4ed3f17
Removed duplicate readasync
HenningNormann Aug 29, 2023
7f9de23
Merge from main
HenningNormann Aug 30, 2023
03c613b
Fixed unit test and sonarcloud change
HenningNormann Aug 30, 2023
ee1553f
Fix for sonarcloud fix and extra condition in query instances db func…
HenningNormann Aug 31, 2023
b8c9599
Case sensitive filtering of instance event event type
HenningNormann Sep 1, 2023
e6b1950
Renamed internalId to instanceInternalId
HenningNormann Sep 4, 2023
5c6a5f4
Merge from main
HenningNormann Sep 5, 2023
353a7e4
Moved blob functionality to new repository
HenningNormann Sep 8, 2023
f93972f
Added CleanupController and related db logic. Cut-and-paste from exis…
HenningNormann Sep 18, 2023
2071363
Added blob repository to the unit tests
HenningNormann Sep 18, 2023
17e521f
- Changed http verb in cleanup controller from get to delete
HenningNormann Sep 19, 2023
d192cd8
Added CleanupController.CleanupInstancesForApp
HenningNormann Sep 19, 2023
82a401f
Added app insights dependency tracking for postgres
HenningNormann Sep 20, 2023
3029637
Merge from main
HenningNormann Sep 21, 2023
cb6518a
Avoid call to SetStatuses when no instances are found
HenningNormann Sep 22, 2023
c12458e
Fix to avoid losing date query parameters when both "from" and "to" a…
HenningNormann Sep 22, 2023
703ecab
Merge from main
HenningNormann Oct 30, 2023
a871278
Merge from main
HenningNormann Nov 23, 2023
50a0d96
Comply to dispose pattern in TelemetryTracker
HenningNormann Nov 23, 2023
4c281f2
Fixed invalid table reference
HenningNormann Nov 24, 2023
44e425f
Added UsePostgreSQL config to CleanupController
HenningNormann Nov 28, 2023
788c294
Merge remote-tracking branch 'origin/feature/191-postgresql-poc-4' in…
HenningNormann Nov 29, 2023
f93d6db
Added InstanceTests
HenningNormann Dec 1, 2023
0f6b92c
Added tests for DataElement
HenningNormann Dec 1, 2023
25dfa53
Added tests for InstanceEvent
HenningNormann Dec 4, 2023
de703b5
Added tests for application
HenningNormann Dec 4, 2023
d5033ab
Added texts tests
HenningNormann Dec 5, 2023
5a46a0a
- storage.readallformultipledataelement parameter fix
HenningNormann Dec 5, 2023
74a3ff4
Code smells
HenningNormann Dec 5, 2023
216b180
Code smells and formatting
HenningNormann Dec 5, 2023
238e602
Suggestions from code review
HenningNormann Dec 5, 2023
090cb11
Merge from main
HenningNormann Dec 5, 2023
e70ff90
Added postgres to build pipeline
HenningNormann Dec 6, 2023
f1612c0
Changed MigrationScriptPath
HenningNormann Dec 6, 2023
3c0687d
Added cloning of instances to avoid having the id broken
HenningNormann Dec 6, 2023
c14fa43
Debugging instance id
HenningNormann Dec 6, 2023
69c0d33
Added postgres to modified build-and-analyze.yml
HenningNormann Dec 7, 2023
422e8a5
Changed java version
HenningNormann Dec 7, 2023
48a6bb3
Resolved merge conflict
HenningNormann Dec 7, 2023
1e1250e
Changed order of postgres prosessing
HenningNormann Dec 7, 2023
04929ee
Removed swagger from exclude list
HenningNormann Dec 7, 2023
023c6c1
attempting to exclude sql files from all analysis
acn-sbuad Dec 7, 2023
3e2a26d
excluding all migration script files
acn-sbuad Dec 7, 2023
8acc12f
removed extra space
acn-sbuad Dec 7, 2023
d0160f1
testing new format for exclude
acn-sbuad Dec 7, 2023
abd5a8d
general exclusion for migration folder
acn-sbuad Dec 7, 2023
db05e6f
Fixed unit tests
HenningNormann Dec 7, 2023
a42f874
Removed debug logic
HenningNormann Dec 7, 2023
4d4d6e0
Code smell
HenningNormann Dec 7, 2023
6bc6e1b
Update src/Storage/Controllers/CleanupController.cs
HenningNormann Dec 11, 2023
4195b61
Update src/Storage/Controllers/CleanupController.cs
HenningNormann Dec 11, 2023
d4b7550
Update src/Storage/Controllers/CleanupController.cs
HenningNormann Dec 11, 2023
497f726
Update src/Storage/Controllers/CleanupController.cs
HenningNormann Dec 11, 2023
b439c05
Update src/Storage/Controllers/CleanupController.cs
HenningNormann Dec 11, 2023
a05d47a
Update src/Storage/Controllers/CleanupController.cs
HenningNormann Dec 11, 2023
ce9273f
Update src/Storage/Controllers/CleanupController.cs
HenningNormann Dec 11, 2023
5f63b01
Update src/Storage/Controllers/CleanupController.cs
HenningNormann Dec 11, 2023
1df4eb9
- Made suggested changes to cleanupcontroller consistent
HenningNormann Dec 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion .github/workflows/build-and-analyze.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ jobs:
name: Build, test & analyze
if: ((github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) || github.event_name == 'push') && github.repository_owner == 'Altinn' && github.actor != 'dependabot[bot]'
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_USER: platform_storage_admin
POSTGRES_PASSWORD: Password
POSTGRES_DB: storagedb
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- name: Setup .NET
uses: actions/setup-dotnet@v3
Expand All @@ -35,6 +49,10 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Setup PostgreSQL
run: |
chmod +x dbsetup.sh
./dbsetup.sh
- name: Cache SonarCloud packages
uses: actions/cache@v3
with:
Expand All @@ -49,7 +67,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
dotnet-sonarscanner begin /k:"Altinn_altinn-storage" /o:"altinn" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.vstest.reportsPaths="**/*.trx" /d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml" /d:sonar.cpd.exclusions="**/Swagger/*Filter.cs,src/Storage/Migration/**/*.sql" /d:sonar.coverage.exclusions="src/Storage/Migration/"
dotnet-sonarscanner begin /k:"Altinn_altinn-storage" /o:"altinn" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.vstest.reportsPaths="**/*.trx" /d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml" /d:sonar.exclusions="src/Storage/Migration/**/*"

dotnet build Altinn.Platform.Storage.sln -v q

Expand Down
9 changes: 9 additions & 0 deletions dbsetup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
export PGPASSWORD=Password

# set up platform_storage role
psql -h localhost -p 5432 -U platform_storage_admin -d storagedb \
-c "DO \$\$
BEGIN CREATE ROLE platform_storage WITH LOGIN PASSWORD 'Password';
EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END \$\$;"
2 changes: 1 addition & 1 deletion src/DataCleanup/NightlyCleanup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@
{
List<Instance> instances = await _cosmosService.GetHardDeletedInstances();
List<Application> applications = await _cosmosService.GetApplications(instances.Select(i => i.AppId).Distinct().ToList());
List<string> autoDeleteAppIds = applications.Where(a => a.AutoDeleteOnProcessEnd == true).Select(a => a.Id).ToList();

Check warning on line 48 in src/DataCleanup/NightlyCleanup.cs

View workflow job for this annotation

GitHub Actions / Build, test & analyze

Remove the unnecessary Boolean literal(s). (https://rules.sonarsource.com/csharp/RSPEC-1125)
int successfullyDeleted = 0;

Stopwatch stopwatch = new Stopwatch();
Stopwatch stopwatch = new();
stopwatch.Start();

foreach (Instance instance in instances)
Expand Down
245 changes: 245 additions & 0 deletions src/Storage/Controllers/CleanupController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Altinn.Platform.Storage.Configuration;
using Altinn.Platform.Storage.Interface.Models;
using Altinn.Platform.Storage.Repository;

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;

namespace Altinn.Platform.Storage.Controllers
{
/// <summary>
/// Handles cleanup of storage data
/// </summary>
[Route("storage/api/v1/cleanup")]
[ApiController]
public class CleanupController : ControllerBase
{
private readonly IInstanceRepository _instanceRepository;
private readonly IApplicationRepository _applicationRepository;
private readonly IBlobRepository _blobRepository;
private readonly IDataRepository _dataRepository;
private readonly IInstanceEventRepository _instanceEventRepository;
private readonly ILogger<CleanupController> _logger;
private readonly bool _usePostgreSQL;

/// <summary>
/// Initializes a new instance of the <see cref="CleanupController"/> class
/// </summary>
/// <param name="instanceRepository">the instance repository handler</param>
/// <param name="applicationRepository">the application repository handler</param>
/// <param name="blobRepository">the blob repository handler</param>
/// <param name="dataRepository">the data repository handler</param>
/// <param name="instanceEventRepository">the instance event repository handler</param>
/// <param name="logger">the logger</param>
/// <param name="generalSettings">the general settings.</param>
public CleanupController(
IInstanceRepository instanceRepository,
IApplicationRepository applicationRepository,
IBlobRepository blobRepository,
IDataRepository dataRepository,
IInstanceEventRepository instanceEventRepository,
ILogger<CleanupController> logger,
IOptions<GeneralSettings> generalSettings)
{
_instanceRepository = instanceRepository;
_applicationRepository = applicationRepository;
_blobRepository = blobRepository;
_dataRepository = dataRepository;
_instanceEventRepository = instanceEventRepository;
_logger = logger;
_usePostgreSQL = generalSettings.Value.UsePostgreSQL;
}

/// <summary>
/// Invoke periodic cleanup of instances
/// </summary>
/// <returns>?</returns>
[HttpDelete("cleanupinstances")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<ActionResult> CleanupInstances()
{
if (!_usePostgreSQL)
{
return Ok();
}

List<Instance> instances = await _instanceRepository.GetHardDeletedInstances();
List<string> autoDeleteAppIds = (await _applicationRepository.FindAll())
.Where(a => instances.Select(i => i.AppId).ToList().Contains(a.Id) && a.AutoDeleteOnProcessEnd)
.Select(a => a.Id).ToList();

Stopwatch stopwatch = Stopwatch.StartNew();
int successfullyDeleted = await CleanupInstancesInternal(instances, autoDeleteAppIds, "NightlyCleanup");
stopwatch.Stop();

_logger.LogInformation(
"CleanupController// CleanupInstances // {DeleteCount} of {OriginalCount} instances deleted in {Duration} s",
successfullyDeleted,
instances.Count,
stopwatch.Elapsed.TotalSeconds);

return Ok();
}

/// <summary>
/// Invoke periodic cleanup of instances for a specific app
/// </summary>
/// <returns>?</returns>
[HttpDelete("cleanupinstancesforapp/{appId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<ActionResult> CleanupInstancesForApp(string appId)
{
if (!_usePostgreSQL)
{
return Ok();
}

int successfullyDeleted = 0;
int processed = 0;
InstanceQueryResponse instancesResponse = new() { ContinuationToken = null };
Dictionary<string, StringValues> options = new() { { "appId", appId } };

Stopwatch stopwatch = Stopwatch.StartNew();
do
{
instancesResponse = await _instanceRepository.GetInstancesFromQuery(options, instancesResponse.ContinuationToken, 5000);
successfullyDeleted += await CleanupInstancesInternal(instancesResponse.Instances, new List<string>(), nameof(CleanupInstancesForApp));
processed += (int)instancesResponse.Count;
}
while (instancesResponse.ContinuationToken != null);
stopwatch.Stop();

_logger.LogInformation(
"CleanupController // CleanupInstancesForApp // {DeleteCount} of {OriginalCount} instances deleted in {Duration} s",
successfullyDeleted,
processed,
stopwatch.Elapsed.TotalSeconds);

return Ok();
}

/// <summary>
/// Invoke periodic cleanup of data elements
/// </summary>
/// <returns>?</returns>
[HttpDelete("cleanupdataelements")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Produces("application/json")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<ActionResult> CleanupDataelements()
{
if (!_usePostgreSQL)
{
return Ok();
}

List<DataElement> dataElements = await _instanceRepository.GetHardDeletedDataElements();

int successfullyDeleted = 0;

Stopwatch stopwatch = Stopwatch.StartNew();

foreach (DataElement dataElement in dataElements)
{
bool dataBlobDeleted = false;

try
{
dataBlobDeleted = await _blobRepository.DeleteBlob(dataElement.BlobStoragePath.Split('/')[0], dataElement.BlobStoragePath);
if (dataBlobDeleted && await _dataRepository.Delete(dataElement))
{
successfullyDeleted++;
}
}
catch (Exception e)
{
_logger.LogError(
e,
"CleanupController // CleanupDataelements // Error occured when deleting dataElement Id: {dataElement.Id} Blobstoragepath: {blobStoragePath}",
dataElement.Id,
dataElement.BlobStoragePath);
}
}

stopwatch.Stop();
_logger.LogInformation(
"CleanupController // CleanupDataelements // {successfullyDeleted} of {count} data elements deleted in {totalSeconds} s",
successfullyDeleted,
dataElements.Count,
stopwatch.Elapsed.TotalSeconds);

return Ok();
}

private async Task<int> CleanupInstancesInternal(List<Instance> instances, List<string> autoDeleteAppIds, string caller)
HenningNormann marked this conversation as resolved.
Show resolved Hide resolved
{
int successfullyDeleted = 0;
foreach (Instance instance in instances)
{
bool blobsNoException = false;
bool instanceEventsNoException = false;
bool dataElementsNoException = false;

try
{
blobsNoException = await _blobRepository.DeleteDataBlobs(instance);

if (blobsNoException)
{
dataElementsNoException = await _dataRepository.DeleteForInstance(instance.Id.Split('/')[^1]);
}

try
{
if (autoDeleteAppIds.Contains(instance.AppId))
{
await _instanceEventRepository.DeleteAllInstanceEvents(instance.Id);
instanceEventsNoException = true;
}
}
catch (Exception ex)
{
_logger.LogError(
"{Caller} error deleting instance events for id {id}, {message}",
caller,
HenningNormann marked this conversation as resolved.
Show resolved Hide resolved
instance.Id,
ex.Message);
}

if (dataElementsNoException
&& (!autoDeleteAppIds.Contains(instance.AppId) || instanceEventsNoException))
{
await _instanceRepository.Delete(instance);
successfullyDeleted += 1;
_logger.LogInformation(
"{Caller} // Run // Instance deleted: {AppId}/{InstanceId}",
caller,
HenningNormann marked this conversation as resolved.
Show resolved Hide resolved
instance.AppId,
$"{instance.InstanceOwner.PartyId}/{instance.Id}");
}
}
catch (Exception e)
{
_logger.LogError(
e,
"{Caller} // Run // Error occured when deleting instance: {AppId}/{InstanceId}",
caller,
HenningNormann marked this conversation as resolved.
Show resolved Hide resolved
instance.AppId,
$"{instance.InstanceOwner.PartyId}/{instance.Id}");
}
}

return successfullyDeleted;
}
}
}
Loading
Loading