Beispiel #1
0
        ///////////////////////////////////////////////////////////////////////

        #region IServiceProvider Members
        /// <summary>
        /// Gets the service object of the specified type.
        /// </summary>
        /// <param name="serviceType">
        /// An object that specifies the type of service object to get.
        /// </param>
        /// <returns>
        /// A service object of type serviceType -OR- a null reference if
        /// there is no service object of type serviceType.
        /// </returns>
        public object GetService(
            Type serviceType
            )
        {
            if ((serviceType == typeof(ISQLiteSchemaExtensions)) ||
                (serviceType == typeof(DbProviderServices)))
            {
                object result = SQLiteProviderServices.Instance;

                if (SQLite3.ForceLogLifecycle())
                {
                    SQLiteLog.LogMessage(HelperMethods.StringFormat(
                                             CultureInfo.CurrentCulture,
                                             "Success of \"{0}\" from SQLiteProviderFactory.GetService(\"{1}\")...",
                                             (result != null) ? result.ToString() : "<null>",
                                             (serviceType != null) ? serviceType.ToString() : "<null>"));
                }

                return(result);
            }

            if (SQLite3.ForceLogLifecycle())
            {
                SQLiteLog.LogMessage(HelperMethods.StringFormat(
                                         CultureInfo.CurrentCulture,
                                         "Failure of SQLiteProviderFactory.GetService(\"{0}\")...",
                                         (serviceType != null) ? serviceType.ToString() : "<null>"));
            }

            return(null);
        }
Beispiel #2
0
        ///////////////////////////////////////////////////////////////////////

        #region IServiceProvider Members
        /// <summary>
        /// Gets the service object of the specified type.
        /// </summary>
        /// <param name="serviceType">
        /// An object that specifies the type of service object to get.
        /// </param>
        /// <returns>
        /// A service object of type serviceType -OR- a null reference if
        /// there is no service object of type serviceType.
        /// </returns>
        public object GetService(
            Type serviceType
            )
        {
            if ((serviceType == typeof(ISQLiteSchemaExtensions)) ||
                (serviceType == typeof(DbProviderServices)))
            {
                SQLiteLog.LogMessage(HelperMethods.StringFormat(
                                         CultureInfo.CurrentCulture,
                                         "IServiceProvider.GetService for type \"{0}\" (success).",
                                         serviceType));

                return(SQLiteProviderServices.Instance);
            }

            SQLiteLog.LogMessage(HelperMethods.StringFormat(
                                     CultureInfo.CurrentCulture,
                                     "IServiceProvider.GetService for type \"{0}\" (failure).",
                                     serviceType));

            return(null);
        }
Beispiel #3
0
        /// <summary>
        /// Creates a new SQLiteDatabase instance.
        /// </summary>
        public SQLiteDatabase()
        {
            try
            {
                _importingShareIds    = new HashSet <Guid>();
                _maintenanceScheduler = new ActionBlock <bool>(async _ => await PerformDatabaseMaintenanceAsync(), new ExecutionDataflowBlockOptions {
                    BoundedCapacity = 2
                });
                _messageQueue = new AsynchronousMessageQueue(this, new[] { ContentDirectoryMessaging.CHANNEL });
                _messageQueue.MessageReceived += OnMessageReceived;
                _messageQueue.Start();

                _settings = ServiceRegistration.Get <ISettingsManager>().Load <SQLiteSettings>();
                _settings.LogSettings();

                LogVersionInformation();

                if (_settings.EnableTraceLogging)
                {
                    _sqliteDebugLogger = FileLogger.CreateFileLogger(ServiceRegistration.Get <IPathManager>().GetPath(@"<LOG>\SQLiteDebug.log"), LogLevel.Debug, false, true);
                    SQLiteLog.Initialize();
                    SQLiteLog.RemoveDefaultHandler();
                    SQLiteLog.Log += MPSQLiteLogEventHandler;
                }

                var    pathManager   = ServiceRegistration.Get <IPathManager>();
                string dataDirectory = pathManager.GetPath("<DATABASE>");
                string databaseFile  = Path.Combine(dataDirectory, _settings.DatabaseFileName);

                // We use an URI instead of a simple database path and filename. The reason is that
                // only this way we can switch on the shared cache mode of SQLite in System.Data.SQLite
                // However, when using an URI, SQLite ignores the page size value specified in the connection string.
                // Therefore we have to use a workaround below to create a database file with the specified page size.
                string databaseFileForUri = databaseFile.Replace('\\', '/');
                string databaseUri        = System.Web.HttpUtility.UrlPathEncode("file:///" + databaseFileForUri + "?cache=shared");

#if !NO_POOL
                _connectionPool = new ConnectionPool <SQLiteConnection>(CreateOpenAndInitializeConnection);
#endif

                // We are using the ConnectionStringBuilder to generate the connection string
                // This ensures code compatibility in case of changes to the SQLite connection string parameters
                // More information on the parameters can be found here: http://www.sqlite.org/pragma.html
                var connBuilder = new SQLiteConnectionStringBuilder
                {
                    // Name of the database file including path as URI
                    FullUri = databaseUri,

                    // Use SQLite database version 3.x
                    Version = 3,

                    // Store GUIDs as binaries, not as string
                    // Saves some space in the database and is said to make search queries on GUIDs faster
                    BinaryGUID = true,

                    DefaultTimeout = _settings.LockTimeout,
                    CacheSize      = _settings.CacheSizeInPages,

                    // Use the Write Ahead Log mode
                    // In this journal mode write locks do not block reads
                    // Needed to prevent sluggish behaviour of MP2 client when trying to read from the database (through MP2 server)
                    // while MP2 server writes to the database (such as when importing shares)
                    // More information can be found here: http://www.sqlite.org/wal.html
                    JournalMode = SQLiteJournalModeEnum.Wal,

                    // Do not use the inbuilt connection pooling of System.Data.SQLite
                    // We use our own connection pool which is faster.
#if NO_POOL
                    Pooling = true,
#else
                    Pooling = false,
#endif

                    // Sychronization Mode "Normal" enables parallel database access while at the same time preventing database
                    // corruption and is therefore a good compromise between "Off" (more performance) and "On"
                    // More information can be found here: http://www.sqlite.org/pragma.html#pragma_synchronous
                    SyncMode = SynchronizationModes.Normal,

                    // MP2's database backend uses foreign key constraints to ensure referential integrity.
                    // SQLite supports this, but it has to be enabled for each database connection by a PRAGMA command
                    // For details see http://www.sqlite.org/foreignkeys.html
                    ForeignKeys = true
                };

                if (_settings.EnableTraceLogging)
                {
                    connBuilder.Flags = SQLiteConnectionFlags.LogAll;
                }

                _connectionString = connBuilder.ToString();
                ServiceRegistration.Get <ILogger>().Info("SQLiteDatabase: Connection String used: '{0}'", _connectionString);

                if (!File.Exists(databaseFile))
                {
                    ServiceRegistration.Get <ILogger>().Info("SQLiteDatabase: Database file does not exists. Creating database file");

                    if (!Directory.Exists(dataDirectory))
                    {
                        Directory.CreateDirectory(dataDirectory);
                    }

                    // Since we use an URI in the standard connection string and system.data.sqlite therefore
                    // ignores the page size value in the connection string, this is a workaroung to make sure
                    // the page size value as specified in the settings is used. When the database file does
                    // not yet exists, we create a special connection string where we additionally set the
                    // datasource (which overrides the URI) and the page size. We then create a connection with
                    // that special connection string, open it and close it. That way the database file is created
                    // with the desired page size. The page size is permanently stored in the database file so that
                    // it is used when as of now we use connections with URI.
                    connBuilder.DataSource = databaseFile;
                    connBuilder.PageSize   = _settings.PageSize;
                    using (var connection = new SQLiteConnection(connBuilder.ToString()))
                    {
                        connection.Open();
                        connection.Close();
                    }
                }

                // The following is necessary to avoid the creation of of a shared memory index file
                // ("-shm"-file) when using exclusive locking mode. When WAL-mode is used, it is possible
                // to switch between normal and exclusive locking mode at any time. However, the creation
                // of a "-shm"-file can only be avoided, when exclusive locking mode is set BEFORE entering
                // WAL-mode. If this is the case, it is not possible to leave exclusive locking mode
                // without leaving WAL-mode before, because the "-shm"-file was not created.
                // The regular connections in our connection pool use WAL-mode. Therefore we have
                // to open one connection without WAL-Mode (here with JournalMode=OFF) and set locking_mode=
                // EXCLUSIVE before we create the first regular connection that goes into the pool.
                // To use exclusive locking mode, it is additionally necessary to set locking_mode=EXCLUSIVE
                // for every connection in the pool via the InitializationCommand. If "PRAGMA locking_mode=
                // EXCLUSIVE" is not in the InitializationCommand, normal locking mode is used
                // although we issue "PRAGMA locking_mode=EXCLUSIVE" at this point.
                // For details see here: http://sqlite.org/wal.html#noshm
                // Avoiding the creation of an "-shm"-file materially improves the database performance.
                if (_settings.UseExclusiveMode)
                {
                    connBuilder.JournalMode = SQLiteJournalModeEnum.Off;
                    using (var connection = new SQLiteConnection(connBuilder.ToString()))
                    {
                        connection.Open();
                        using (var command = new SQLiteCommand(SQLiteSettings.EXCLUSIVE_MODE_COMMAND, connection))
                            command.ExecuteNonQuery();
                        connection.Close();
                    }
                }

                // Just test one "regular" connection, which is the first connection in the pool
                using (var transaction = BeginTransaction())
                    transaction.Rollback();
            }
            catch (Exception e)
            {
                ServiceRegistration.Get <ILogger>().Critical("SQLiteDatabase: Error establishing database connection", e);
                throw;
            }
        }