public SQLitePersistentStorage( string workingFolderPath, string solutionFilePath, string databaseFile, IDisposable dbOwnershipLock, IPersistentStorageFaultInjector faultInjectorOpt) : base(workingFolderPath, solutionFilePath, databaseFile) { _dbOwnershipLock = dbOwnershipLock; _faultInjectorOpt = faultInjectorOpt; _solutionAccessor = new SolutionAccessor(this); _projectAccessor = new ProjectAccessor(this); _documentAccessor = new DocumentAccessor(this); _select_star_from_string_table = $@"select * from {StringInfoTableName}"; _insert_into_string_table_values_0 = $@"insert into {StringInfoTableName}(""{DataColumnName}"") values (?)"; _select_star_from_string_table_where_0_limit_one = $@"select * from {StringInfoTableName} where (""{DataColumnName}"" = ?) limit 1"; // Create a queue to batch up requests to flush. We'll won't flush more than every FlushAllDelayMS. The // actual information in the queue isn't relevant, so we pass in an equality comparer to just keep it down // to storing a single piece of data. _flushQueue = new AsyncBatchingWorkQueue <bool>( TimeSpan.FromMilliseconds(FlushAllDelayMS), FlushInMemoryDataToDiskIfNotShutdownAsync, EqualityComparer <bool> .Default, asyncListener: null, _shutdownTokenSource.Token); }
public static SqlConnection Create(IPersistentStorageFaultInjector faultInjector, string databasePath) { faultInjector?.OnNewConnection(); // Allocate dictionary before doing any sqlite work. That way if it throws // we don't have to do any additional cleanup. var queryToStatement = new Dictionary <string, SqlStatement>(); // Use SQLITE_OPEN_NOMUTEX to enable multi-thread mode, where multiple connections can be used provided each // one is only used from a single thread at a time. // see https://sqlite.org/threadsafe.html for more detail var flags = OpenFlags.SQLITE_OPEN_CREATE | OpenFlags.SQLITE_OPEN_READWRITE | OpenFlags.SQLITE_OPEN_NOMUTEX; var handle = NativeMethods.sqlite3_open_v2(databasePath, (int)flags, vfs: null, out var result); if (result != Result.OK) { handle.Dispose(); throw new SqlException(result, $"Could not open database file: {databasePath} ({result})"); } try { NativeMethods.sqlite3_busy_timeout(handle, (int)TimeSpan.FromMinutes(1).TotalMilliseconds); return(new SqlConnection(handle, faultInjector, queryToStatement)); } catch { // If we failed to create connection, ensure that we still release the sqlite // handle. handle.Dispose(); throw; } }
public static SqlConnection Create(IPersistentStorageFaultInjector faultInjector, string databasePath) { faultInjector?.OnNewConnection(); // Allocate dictionary before doing any sqlite work. That way if it throws // we don't have to do any additional cleanup. var queryToStatement = new Dictionary <string, SqlStatement>(); // Use SQLITE_OPEN_NOMUTEX to enable multi-thread mode, where multiple connections can // be used provided each one is only used from a single thread at a time. // // Use SHAREDCACHE so that we can have an in-memory DB that we dump our writes into. We // need SHAREDCACHE so that all connections see that same in-memory DB. This also // requires OPEN_URI since we need a `file::memory:` uri for them all to refer to. // // see https://sqlite.org/threadsafe.html for more detail var flags = OpenFlags.SQLITE_OPEN_CREATE | OpenFlags.SQLITE_OPEN_READWRITE | OpenFlags.SQLITE_OPEN_NOMUTEX | OpenFlags.SQLITE_OPEN_SHAREDCACHE | OpenFlags.SQLITE_OPEN_URI; var result = (Result)raw.sqlite3_open_v2(databasePath, out var handle, (int)flags, vfs: null); if (result != Result.OK) { throw new SqlException(result, $"Could not open database file: {databasePath} ({result})"); } Contract.ThrowIfNull(handle); try { raw.sqlite3_busy_timeout(handle, (int)TimeSpan.FromMinutes(1).TotalMilliseconds); var connection = new SqlConnection(handle, faultInjector, queryToStatement); // Attach (creating if necessary) a singleton in-memory write cache to this connection. // // From: https://www.sqlite.org/sharedcache.html Enabling shared-cache for an in-memory // database allows two or more database connections in the same process to have access // to the same in-memory database. An in-memory database in shared cache is // automatically deleted and memory is reclaimed when the last connection to that // database closes. // // Using `cache=shared as writecache` at the end ensures all connections see the same db // and the same data when reading and writing. i.e. if one connection writes data to // this, another connection will see that data when reading. Without this, each // connection would get their own private memory db independent of all other // connections. connection.ExecuteCommand($"attach database 'file::memory:?cache=shared' as {Database.WriteCache.GetName()};"); return(connection); } catch { // If we failed to create connection, ensure that we still release the sqlite // handle. raw.sqlite3_close(handle); throw; } }
public SQLitePersistentStorageService( IPersistentStorageLocationService locationService, IPersistentStorageFaultInjector faultInjector) : this(locationService) { _faultInjectorOpt = faultInjector; }
internal IPersistentStorage GetStorage( Solution solution, IPersistentStorageFaultInjector faultInjectorOpt = null) { var storage = GetStorageService(faultInjectorOpt).GetStorage(solution); Assert.NotEqual(NoOpPersistentStorage.Instance, storage); return(storage); }
public SQLitePersistentStorageService( IOptionService optionService, IPersistentStorageLocationService locationService, ISolutionSizeTracker solutionSizeTracker, IPersistentStorageFaultInjector faultInjector) : this(optionService, locationService, solutionSizeTracker) { _faultInjectorOpt = faultInjector; }
private SqlConnection(sqlite3 handle, IPersistentStorageFaultInjector faultInjector, Dictionary <string, SqlStatement> queryToStatement) { // This constructor avoids allocations since failure (e.g. OutOfMemoryException) would // leave the object partially-constructed, and the finalizer would run later triggering // a crash. _handle = handle; _faultInjector = faultInjector; _queryToStatement = queryToStatement; }
public SQLitePersistentStorage( IOptionService optionService, string workingFolderPath, string solutionFilePath, string databaseFile, Action <AbstractPersistentStorage> disposer, IPersistentStorageFaultInjector faultInjectorOpt) : base(optionService, workingFolderPath, solutionFilePath, databaseFile, disposer) { _faultInjectorOpt = faultInjectorOpt; _solutionAccessor = new SolutionAccessor(this); _projectAccessor = new ProjectAccessor(this); _documentAccessor = new DocumentAccessor(this); }
public SQLitePersistentStorage( string workingFolderPath, string solutionFilePath, string databaseFile, IDisposable dbOwnershipLock, IPersistentStorageFaultInjector faultInjectorOpt) : base(workingFolderPath, solutionFilePath, databaseFile) { _dbOwnershipLock = dbOwnershipLock; _faultInjectorOpt = faultInjectorOpt; _solutionAccessor = new SolutionAccessor(this); _projectAccessor = new ProjectAccessor(this); _documentAccessor = new DocumentAccessor(this); }
public static SqlConnection Create(IPersistentStorageFaultInjector faultInjector, string databasePath) { faultInjector?.OnNewConnection(); var flags = OpenFlags.SQLITE_OPEN_CREATE | OpenFlags.SQLITE_OPEN_READWRITE; var result = (Result)raw.sqlite3_open_v2(databasePath, out var handle, (int)flags, vfs: null); if (result != Result.OK) { throw new SqlException(result, $"Could not open database file: {databasePath} ({result})"); } Contract.ThrowIfNull(handle); raw.sqlite3_busy_timeout(handle, (int)TimeSpan.FromMinutes(1).TotalMilliseconds); return(new SqlConnection(faultInjector, handle)); }
internal IChecksummedPersistentStorage GetStorageFromKey( Workspace workspace, SolutionKey solutionKey, IPersistentStorageFaultInjector faultInjectorOpt = null) { // If we handed out one for a previous test, we need to shut that down first _storageService?.GetTestAccessor().Shutdown(); var locationService = new MockPersistentStorageLocationService(solutionKey.Id, _persistentFolder.Path); _storageService = GetStorageService(locationService, faultInjectorOpt); var storage = _storageService.GetStorage(workspace, solutionKey, checkBranchId: true); // If we're injecting faults, we expect things to be strange if (faultInjectorOpt == null) { Assert.NotEqual(NoOpPersistentStorage.Instance, storage); } return(storage); }
public SQLitePersistentStorage( string workingFolderPath, string solutionFilePath, string databaseFile, IDisposable dbOwnershipLock, IPersistentStorageFaultInjector faultInjectorOpt) : base(workingFolderPath, solutionFilePath, databaseFile) { _dbOwnershipLock = dbOwnershipLock; _faultInjectorOpt = faultInjectorOpt; _solutionAccessor = new SolutionAccessor(this); _projectAccessor = new ProjectAccessor(this); _documentAccessor = new DocumentAccessor(this); _select_star_from_0 = $@"select * from ""{StringInfoTableName}"""; _insert_into_0_1_values = $@"insert into ""{StringInfoTableName}""(""{DataColumnName}"") values (?)"; _select_star_from_0_where_1_limit_one = $@"select * from ""{StringInfoTableName}"" where (""{DataColumnName}"" = ?) limit 1"; }
public static SqlConnection Create(IPersistentStorageFaultInjector faultInjector, string databasePath) { faultInjector?.OnNewConnection(); // Enable shared cache so that multiple connections inside of same process share cache // see https://sqlite.org/threadsafe.html for more detail var flags = OpenFlags.SQLITE_OPEN_CREATE | OpenFlags.SQLITE_OPEN_READWRITE | OpenFlags.SQLITE_OPEN_NOMUTEX | OpenFlags.SQLITE_OPEN_SHAREDCACHE; var result = (Result)raw.sqlite3_open_v2(databasePath, out var handle, (int)flags, vfs: null); if (result != Result.OK) { throw new SqlException(result, $"Could not open database file: {databasePath} ({result})"); } Contract.ThrowIfNull(handle); raw.sqlite3_busy_timeout(handle, (int)TimeSpan.FromMinutes(1).TotalMilliseconds); return(new SqlConnection(faultInjector, handle)); }
public static SqlConnection Create(IPersistentStorageFaultInjector faultInjector, string databasePath) { faultInjector?.OnNewConnection(); // Use SQLITE_OPEN_NOMUTEX to enable multi-thread mode, where multiple connections can be used provided each // one is only used from a single thread at a time. // see https://sqlite.org/threadsafe.html for more detail var flags = OpenFlags.SQLITE_OPEN_CREATE | OpenFlags.SQLITE_OPEN_READWRITE | OpenFlags.SQLITE_OPEN_NOMUTEX; var result = (Result)raw.sqlite3_open_v2(databasePath, out var handle, (int)flags, vfs: null); if (result != Result.OK) { throw new SqlException(result, $"Could not open database file: {databasePath} ({result})"); } Contract.ThrowIfNull(handle); raw.sqlite3_busy_timeout(handle, (int)TimeSpan.FromMinutes(1).TotalMilliseconds); return(new SqlConnection(faultInjector, handle)); }
internal IChecksummedPersistentStorage GetStorage( Solution solution, IPersistentStorageFaultInjector faultInjectorOpt = null) { // For the sake of tests, all solutions are bigger than our threshold, and thus deserve to get storage for them var solutionSizeTrackerMock = new Mock <ISolutionSizeTracker>(); solutionSizeTrackerMock.Setup(m => m.GetSolutionSize(solution.Workspace, solution.Id)) .Returns(solution.Workspace.Options.GetOption(StorageOptions.SolutionSizeThreshold) + 1); // If we handed out one for a previous test, we need to shut that down first _persistentLocationService?.RaiseShutdown(); _persistentLocationService = new MockPersistentStorageLocationService(solution.Id, _persistentFolder); var storage = GetStorageService(_persistentLocationService, solutionSizeTrackerMock.Object, faultInjectorOpt).GetStorage(solution, checkBranchId: true); // If we're injecting faults, we expect things to be strange if (faultInjectorOpt == null) { Assert.NotEqual(NoOpPersistentStorage.Instance, storage); } return(storage); }
internal abstract IChecksummedPersistentStorageService GetStorageService(IPersistentStorageLocationService locationService, ISolutionSizeTracker solutionSizeTracker, IPersistentStorageFaultInjector faultInjector);
internal abstract AbstractPersistentStorageService GetStorageService(IPersistentStorageLocationService locationService, IPersistentStorageFaultInjector faultInjector);
public SQLitePersistentStorageService(IOptionService optionService, IPersistentStorageFaultInjector faultInjector) : base(optionService, testing: true) { _faultInjectorOpt = faultInjector; }
internal override IPersistentStorageService GetStorageService(IPersistentStorageFaultInjector faultInjector) => new EsentPersistentStorageService(_persistentEnabledOptionService, testing: true);
private SqlConnection(SafeSqliteHandle handle, IPersistentStorageFaultInjector faultInjector, Dictionary <string, SqlStatement> queryToStatement) { _handle = handle; _faultInjector = faultInjector; _queryToStatement = queryToStatement; }
internal override AbstractPersistentStorageService GetStorageService(IMefHostExportProvider exportProvider, IPersistentStorageLocationService locationService, IPersistentStorageFaultInjector faultInjector) => new SQLitePersistentStorageService(locationService, faultInjector);
internal abstract IPersistentStorageService GetStorageService(IPersistentStorageFaultInjector faultInjector);
internal override AbstractPersistentStorageService GetStorageService(IPersistentStorageLocationService locationService, ISolutionSizeTracker solutionSizeTracker, IPersistentStorageFaultInjector faultInjector) => new SQLitePersistentStorageService(_persistentEnabledOptionService, locationService, solutionSizeTracker, faultInjector);
private SqlConnection(IPersistentStorageFaultInjector faultInjector, sqlite3 handle) { _faultInjector = faultInjector; _handle = handle; }
internal override AbstractPersistentStorageService GetStorageService(IPersistentStorageLocationService locationService, IPersistentStorageFaultInjector faultInjector) => new SQLitePersistentStorageService(locationService, faultInjector);
internal override IPersistentStorageService GetStorageService(IPersistentStorageFaultInjector faultInjector) => new SQLitePersistentStorageService(_persistentEnabledOptionService, faultInjector);